symux.c 9.0 KB

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