symux.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * Copyright (c) 2001-2007 Willem Dijkstra
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * - Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * - Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following
  13. * disclaimer in the documentation and/or other materials provided
  14. * with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  19. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  20. * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  22. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  26. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. * POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. */
  30. #include <sys/types.h>
  31. #include <sys/param.h>
  32. #include <sys/socket.h>
  33. #include <netinet/in.h>
  34. #include <netdb.h>
  35. #include <signal.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <sysexits.h>
  40. #include <syslog.h>
  41. #include <unistd.h>
  42. #include <rrd.h>
  43. #include "conf.h"
  44. #include "data.h"
  45. #include "error.h"
  46. #include "limits.h"
  47. #include "symux.h"
  48. #include "symuxnet.h"
  49. #include "net.h"
  50. #include "readconf.h"
  51. #include "share.h"
  52. #include "xmalloc.h"
  53. #include "platform.h"
  54. __BEGIN_DECLS
  55. void exithandler();
  56. void huphandler(int);
  57. void signalhandler(int);
  58. __END_DECLS
  59. int flag_hup = 0;
  60. int flag_testconf = 0;
  61. fd_set fdset;
  62. int maxfd;
  63. void
  64. exithandler(int s)
  65. {
  66. info("received signal %d - quitting", s);
  67. exit(EX_TEMPFAIL);
  68. }
  69. void
  70. huphandler(int s)
  71. {
  72. info("hup received");
  73. flag_hup = 1;
  74. }
  75. /*
  76. * symux is the receiver of symon performance measurements.
  77. *
  78. * The main goals symon hopes to accomplish is:
  79. * - to take fine grained measurements of system parameters
  80. * - with minimal performance impact
  81. * - in a secure way.
  82. *
  83. * Measuring system parameters (e.g. interfaces) sometimes means traversing
  84. * lists in kernel memory. Because of this the measurement of data has been
  85. * decoupled from the processing and storage of data. Storing the measured
  86. * information that symon provides is done by a second program, called symux.
  87. *
  88. * Symon can keep track of cpu, memory, disk and network interface
  89. * interactions. Symon was built specifically for OpenBSD.
  90. */
  91. int
  92. main(int argc, char *argv[])
  93. {
  94. struct packedstream ps;
  95. char *cfgfile;
  96. char *cfgpath = NULL;
  97. char *stringbuf;
  98. char *stringptr;
  99. int maxstringlen;
  100. struct muxlist mul, newmul;
  101. char *arg_ra[4];
  102. struct stream *stream;
  103. struct source *source;
  104. struct sourcelist *sol;
  105. struct mux *mux;
  106. FILE *f;
  107. int ch;
  108. int churnbuflen;
  109. int flag_list;
  110. int offset;
  111. int result;
  112. unsigned int rrderrors;
  113. int slot;
  114. time_t timestamp;
  115. SLIST_INIT(&mul);
  116. /* reset flags */
  117. flag_debug = 0;
  118. flag_daemon = 0;
  119. flag_list = 0;
  120. cfgfile = SYMUX_CONFIG_FILE;
  121. while ((ch = getopt(argc, argv, "df:ltv")) != -1) {
  122. switch (ch) {
  123. case 'd':
  124. flag_debug = 1;
  125. break;
  126. case 'f':
  127. if (optarg && optarg[0] != '/') {
  128. /* cfg path needs to be absolute, we will be a daemon soon */
  129. cfgpath = xmalloc(MAX_PATH_LEN);
  130. if ((cfgpath = getcwd(cfgpath, MAX_PATH_LEN)) == NULL)
  131. fatal("could not get working directory");
  132. maxstringlen = strlen(cfgpath) + 1 + strlen(optarg) + 1;
  133. cfgfile = xmalloc(maxstringlen);
  134. strncpy(cfgfile, cfgpath, maxstringlen - 1);
  135. stringptr = cfgfile + strlen(cfgpath);
  136. stringptr[0] = '/';
  137. stringptr++;
  138. strncpy(stringptr, optarg, maxstringlen - 1 - (stringptr - cfgfile));
  139. cfgfile[maxstringlen - 1] = '\0';
  140. xfree(cfgpath);
  141. } else
  142. cfgfile = xstrdup(optarg);
  143. break;
  144. case 'l':
  145. flag_list = 1;
  146. break;
  147. case 't':
  148. flag_testconf = 1;
  149. break;
  150. case 'v':
  151. info("symux version %s", SYMUX_VERSION);
  152. default:
  153. info("usage: %s [-d] [-l] [-v] [-f cfgfile]", __progname);
  154. exit(EX_USAGE);
  155. }
  156. }
  157. if (flag_list == 1) {
  158. /* read configuration without file checks */
  159. result = read_config_file(&mul, cfgfile, 0);
  160. if (!result) {
  161. fatal("configuration contained errors; quitting");
  162. }
  163. mux = SLIST_FIRST(&mul);
  164. if (mux == NULL) {
  165. fatal("%s:%d: mux not found", __FILE__, __LINE__);
  166. }
  167. sol = &mux->sol;
  168. if (sol == NULL) {
  169. fatal("%s:%d: sourcelist not found", __FILE__, __LINE__);
  170. }
  171. SLIST_FOREACH(source, sol, sources) {
  172. if (! SLIST_EMPTY(&source->sl)) {
  173. SLIST_FOREACH(stream, &source->sl, streams) {
  174. if (stream->file != NULL) {
  175. info("%.200s", stream->file);
  176. }
  177. }
  178. }
  179. }
  180. return (EX_OK);
  181. } else {
  182. /* read configuration file with file access checks */
  183. result = read_config_file(&mul, cfgfile, 1);
  184. if (!result) {
  185. fatal("configuration contained errors; quitting");
  186. }
  187. }
  188. if (flag_testconf) {
  189. info("%s: ok", cfgfile);
  190. exit(EX_OK);
  191. }
  192. setegid(getgid());
  193. setgid(getgid());
  194. if (flag_debug != 1) {
  195. if (daemon(0, 0) != 0)
  196. fatal("daemonize failed");
  197. flag_daemon = 1;
  198. /* record pid */
  199. f = fopen(SYMUX_PID_FILE, "w");
  200. if (f) {
  201. fprintf(f, "%u\n", (u_int) getpid());
  202. fclose(f);
  203. }
  204. }
  205. info("symux version %s", SYMUX_VERSION);
  206. if (flag_debug == 1)
  207. info("program id=%d", (u_int) getpid());
  208. mux = SLIST_FIRST(&mul);
  209. churnbuflen = strlen_sourcelist(&mux->sol);
  210. debug("size of churnbuffer = %d", churnbuflen);
  211. initshare(churnbuflen);
  212. init_symux_packet(mux);
  213. /* catch signals */
  214. signal(SIGHUP, huphandler);
  215. signal(SIGINT, exithandler);
  216. signal(SIGQUIT, exithandler);
  217. signal(SIGTERM, exithandler);
  218. signal(SIGTERM, exithandler);
  219. /* prepare crc32 */
  220. init_crc32();
  221. /* prepare sockets */
  222. if (get_symon_sockets(mux) == 0)
  223. fatal("no sockets could be opened for incoming symon traffic");
  224. if (get_client_socket(mux) == 0)
  225. fatal("socket for client connections could not be opened");
  226. rrderrors = 0;
  227. /* main loop */
  228. for (;;) { /* FOREVER */
  229. wait_for_traffic(mux, &source);
  230. if (flag_hup == 1) {
  231. flag_hup = 0;
  232. SLIST_INIT(&newmul);
  233. if (!read_config_file(&newmul, cfgfile, 1)) {
  234. info("new configuration contains errors; keeping old configuration");
  235. free_muxlist(&newmul);
  236. } else {
  237. info("read configuration file '%.100s' successfully", cfgfile);
  238. free_muxlist(&mul);
  239. mul = newmul;
  240. mux = SLIST_FIRST(&mul);
  241. get_symon_sockets(mux);
  242. get_client_socket(mux);
  243. init_symux_packet(mux);
  244. }
  245. } else {
  246. /*
  247. * Put information from packet into stringbuf (shared region).
  248. * Note that the stringbuf is used twice: 1) to update the
  249. * rrdfile and 2) to collect all the data from a single packet
  250. * that needs to shared to the clients. This is the reason for
  251. * the hasseling with stringptr.
  252. */
  253. offset = mux->packet.offset;
  254. maxstringlen = shared_getmaxlen();
  255. /* put time:ip: into shared region */
  256. slot = master_forbidread();
  257. timestamp = (time_t) mux->packet.header.timestamp;
  258. stringbuf = shared_getmem(slot);
  259. debug("stringbuf = 0x%08x", stringbuf);
  260. snprintf(stringbuf, maxstringlen, "%s;", source->addr);
  261. /* hide this string region from rrd update */
  262. maxstringlen -= strlen(stringbuf);
  263. stringptr = stringbuf + strlen(stringbuf);
  264. while (offset < mux->packet.header.length) {
  265. bzero(&ps, sizeof(struct packedstream));
  266. if (mux->packet.header.symon_version == 1) {
  267. offset += sunpack1(mux->packet.data + offset, &ps);
  268. } else if (mux->packet.header.symon_version == 2) {
  269. offset += sunpack2(mux->packet.data + offset, &ps);
  270. } else {
  271. debug("unsupported packet version - ignoring data");
  272. ps.type = MT_EOT;
  273. }
  274. /* find stream in source */
  275. stream = find_source_stream(source, ps.type, ps.arg);
  276. if (stream != NULL) {
  277. /* put type and arg in and hide from rrd */
  278. snprintf(stringptr, maxstringlen, "%s:%s:", type2str(ps.type), ps.arg);
  279. maxstringlen -= strlen(stringptr);
  280. stringptr += strlen(stringptr);
  281. /* put timestamp in and show to rrd */
  282. snprintf(stringptr, maxstringlen, "%u", (unsigned int)timestamp);
  283. arg_ra[3] = stringptr;
  284. maxstringlen -= strlen(stringptr);
  285. stringptr += strlen(stringptr);
  286. /* put measurements in */
  287. ps2strn(&ps, stringptr, maxstringlen, PS2STR_RRD);
  288. if (stream->file != NULL) {
  289. /* clear optind for getopt call by rrdupdate */
  290. optind = 0;
  291. /* save if file specified */
  292. arg_ra[0] = "rrdupdate";
  293. arg_ra[1] = "--";
  294. arg_ra[2] = stream->file;
  295. /*
  296. * This call will cost a lot (symux will become
  297. * unresponsive and eat up massive amounts of cpu) if
  298. * the rrdfile is out of sync.
  299. */
  300. rrd_update(4, arg_ra);
  301. if (rrd_test_error()) {
  302. if (rrderrors < SYMUX_MAXRRDERRORS) {
  303. rrderrors++;
  304. warning("rrd_update:%.200s", rrd_get_error());
  305. warning("%.200s %.200s %.200s %.200s", arg_ra[0], arg_ra[1],
  306. arg_ra[2], arg_ra[3]);
  307. if (rrderrors == SYMUX_MAXRRDERRORS) {
  308. warning("maximum rrd errors reached - will stop reporting them");
  309. }
  310. }
  311. rrd_clear_error();
  312. } else {
  313. if (flag_debug == 1)
  314. debug("%.200s %.200s %.200s %.200s", arg_ra[0], arg_ra[1],
  315. arg_ra[2], arg_ra[3]);
  316. }
  317. }
  318. maxstringlen -= strlen(stringptr);
  319. stringptr += strlen(stringptr);
  320. snprintf(stringptr, maxstringlen, ";");
  321. maxstringlen -= strlen(stringptr);
  322. stringptr += strlen(stringptr);
  323. } else {
  324. debug("ignored unaccepted stream %.16s(%.16s) from %.20s", type2str(ps.type),
  325. ((ps.arg == NULL) ? "0" : ps.arg), source->addr);
  326. }
  327. }
  328. /*
  329. * packet = parsed and in ascii in shared region -> copy to
  330. * clients
  331. */
  332. snprintf(stringptr, maxstringlen, "\n");
  333. stringptr += strlen(stringptr);
  334. shared_setlen(slot, (stringptr - stringbuf));
  335. debug("churnbuffer used: %d", (stringptr - stringbuf));
  336. master_permitread();
  337. } /* flag_hup == 0 */
  338. } /* forever */
  339. /* NOT REACHED */
  340. return (EX_SOFTWARE);
  341. }