monmux.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /* $Id: monmux.c,v 1.20 2002/08/26 14:53:01 dijkstra Exp $ */
  2. /*
  3. * Copyright (c) 2001-2002 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. * - 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/socket.h>
  32. #include <netinet/in.h>
  33. #include <netdb.h>
  34. #include <machine/endian.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <sysexits.h>
  39. #include <syslog.h>
  40. #include <unistd.h>
  41. #include <rrd.h>
  42. #include "data.h"
  43. #include "error.h"
  44. #include "limits.h"
  45. #include "monmux.h"
  46. #include "muxnet.h"
  47. #include "net.h"
  48. #include "readconf.h"
  49. #include "share.h"
  50. #include "xmalloc.h"
  51. __BEGIN_DECLS
  52. void exithandler();
  53. void huphandler(int);
  54. void signalhandler(int);
  55. __END_DECLS
  56. int flag_hup = 0;
  57. fd_set fdset;
  58. int maxfd;
  59. void
  60. exithandler(int s) {
  61. info("received signal %d - quitting", s);
  62. exit(EX_TEMPFAIL);
  63. }
  64. void
  65. huphandler(int s) {
  66. info("sighup (%d) received", s);
  67. flag_hup = 1;
  68. }
  69. /*
  70. * Monmux is the receiver of mon performance measurements.
  71. *
  72. * The main goals mon hopes to accomplish is:
  73. * - to take fine grained measurements of system parameters
  74. * - with minimal performance impact
  75. * - in a secure way.
  76. *
  77. * Measuring system parameters (e.g. interfaces) sometimes means traversing
  78. * lists in kernel memory. Because of this the measurement of data has been
  79. * decoupled from the processing and storage of data. Storing the measured
  80. * information that mon provides is done by a second program, called monmux.
  81. *
  82. * Mon can keep track of cpu, memory, disk and network interface
  83. * interactions. Mon was built specifically for OpenBSD.
  84. */
  85. int
  86. main(int argc, char *argv[])
  87. {
  88. struct monpacket packet;
  89. struct packedstream ps;
  90. char *cfgfile;
  91. char *cfgpath;
  92. char *stringbuf;
  93. char *stringptr;
  94. int maxstringlen;
  95. struct muxlist mul, newmul;
  96. struct sourcelist sol, newsol;
  97. char *arg_ra[4];
  98. struct stream *stream;
  99. struct source *source;
  100. struct mux *mux;
  101. FILE *f;
  102. int ch;
  103. int offset;
  104. time_t timestamp;
  105. int churnbuflen;
  106. SLIST_INIT(&mul);
  107. SLIST_INIT(&sol);
  108. /* reset flags */
  109. flag_debug = 0;
  110. flag_daemon = 0;
  111. cfgfile = MONMUX_CONFIG_FILE;
  112. while ((ch = getopt(argc, argv, "dvf:")) != -1) {
  113. switch (ch) {
  114. case 'd':
  115. flag_debug = 1;
  116. break;
  117. case 'f':
  118. if (optarg && optarg[1] != '/') {
  119. /* cfg path needs to be absolute, we will be a daemon soon */
  120. if ((cfgpath = getwd(NULL)) == NULL)
  121. fatal("could not get working directory");
  122. maxstringlen = strlen(cfgpath) + strlen(optarg) + 1;
  123. cfgfile = xmalloc(maxstringlen);
  124. strncpy(cfgfile, cfgpath, maxstringlen);
  125. stringptr = cfgfile + strlen(cfgpath);
  126. stringptr[0] = '/';
  127. stringptr++;
  128. strncpy(stringptr, optarg, maxstringlen - (cfgfile - stringptr));
  129. cfgfile[maxstringlen] = '\0';
  130. free(cfgpath);
  131. } else
  132. cfgfile = xstrdup(optarg);
  133. break;
  134. case 'v':
  135. info("monmux version %s", MONMUX_VERSION);
  136. default:
  137. info("usage: %s [-d] [-v] [-f cfgfile]", __progname);
  138. exit(EX_USAGE);
  139. }
  140. }
  141. if (flag_debug != 1) {
  142. if (daemon(0,0) != 0)
  143. fatal("daemonize failed");
  144. flag_daemon = 1;
  145. /* record pid */
  146. f = fopen(MONMUX_PID_FILE, "w");
  147. if (f) {
  148. fprintf(f, "%u\n", (u_int) getpid());
  149. fclose(f);
  150. }
  151. }
  152. info("monmux version %s", MONMUX_VERSION);
  153. /* parse configuration file */
  154. if (!read_config_file(&mul, &sol, cfgfile))
  155. fatal("configuration contained errors; quitting");
  156. if (flag_debug == 1)
  157. info("program id=%d", (u_int) getpid());
  158. mux = SLIST_FIRST(&mul);
  159. churnbuflen = calculate_churnbuffer(&sol);
  160. debug("size of churnbuffer = %d", churnbuflen);
  161. initshare(churnbuflen);
  162. /* catch signals */
  163. signal(SIGHUP, huphandler);
  164. signal(SIGINT, exithandler);
  165. signal(SIGQUIT, exithandler);
  166. signal(SIGTERM, exithandler);
  167. signal(SIGTERM, exithandler);
  168. /* Prepare crc32 */
  169. init_crc32();
  170. getmonsocket(mux);
  171. getclientsocket(mux);
  172. for (;;) {
  173. waitfortraffic(mux, &sol, &source, &packet);
  174. if (flag_hup == 1) {
  175. flag_hup = 0;
  176. SLIST_INIT(&newmul);
  177. SLIST_INIT(&newsol);
  178. if (!read_config_file(&newmul, &newsol, cfgfile)) {
  179. info("new configuration contains errors; keeping old configuration");
  180. free_muxlist(&newmul);
  181. free_sourcelist(&newsol);
  182. } else {
  183. info("read configuration file succesfully");
  184. free_muxlist(&mul);
  185. free_sourcelist(&sol);
  186. mul = newmul;
  187. sol = newsol;
  188. mux = SLIST_FIRST(&mul);
  189. getmonsocket(mux);
  190. getclientsocket(mux);
  191. }
  192. break; /* wait for next alarm */
  193. }
  194. /* Put information from packet into stringbuf (shared region). Note
  195. * that the stringbuf is used twice: 1) to update the rrdfile and 2) to
  196. * collect all the data from a single packet that needs to shared to
  197. * the clients. This is the reason for the hasseling with stringptr.
  198. */
  199. offset = 0;
  200. maxstringlen = shared_getmaxlen();
  201. /* put time:ip: into shared region */
  202. master_forbidread();
  203. timestamp = (time_t) packet.header.timestamp;
  204. stringbuf = (char *)shared_getmem();
  205. snprintf(stringbuf, maxstringlen, "%u.%u.%u.%u;",
  206. IPAS4BYTES(source->ip));
  207. /* hide this string region from rrd update */
  208. maxstringlen -= strlen(stringbuf);
  209. stringptr = stringbuf + strlen(stringbuf);
  210. while (offset < packet.header.length) {
  211. offset += sunpack(packet.data + offset, &ps);
  212. /* find stream in source */
  213. stream = find_source_stream(source, ps.type, ps.args);
  214. if (stream != NULL) {
  215. /* put type in and hide from rrd */
  216. snprintf(stringptr, maxstringlen, "%s:", type2str(ps.type));
  217. maxstringlen -= strlen(stringptr);
  218. stringptr += strlen(stringptr);
  219. /* put arguments in and hide from rrd */
  220. snprintf(stringptr, maxstringlen, "%s:",
  221. ((ps.args == NULL) ? "0" : ps.args));
  222. maxstringlen -= strlen(stringptr);
  223. stringptr += strlen(stringptr);
  224. /* put timestamp in and show to rrd */
  225. snprintf(stringptr, maxstringlen, "%u", timestamp);
  226. arg_ra[3] = stringptr;
  227. maxstringlen -= strlen(stringptr);
  228. stringptr += strlen(stringptr);
  229. /* put measurements in */
  230. ps2strn(&ps, stringptr, maxstringlen, PS2STR_RRD);
  231. if (stream->file != NULL) {
  232. /* save if file specified */
  233. arg_ra[0] = "rrdupdate";
  234. arg_ra[1] = "--";
  235. arg_ra[2] = stream->file;
  236. /* This call will cost a lot (monmux will become
  237. * unresponsive and eat up massive amounts of cpu) if
  238. * the rrdfile is out of sync. While I could update the
  239. * rrd in a separate process, I choose not to at this
  240. * time.
  241. */
  242. rrd_update(4, arg_ra);
  243. if (rrd_test_error()) {
  244. warning("rrd_update:%s", rrd_get_error());
  245. warning("%s %s %s %s", arg_ra[0], arg_ra[1],
  246. arg_ra[2], arg_ra[3]);
  247. rrd_clear_error();
  248. } else {
  249. if (flag_debug == 1)
  250. debug("%s %s %s %s", arg_ra[0], arg_ra[1],
  251. arg_ra[2], arg_ra[3]);
  252. }
  253. }
  254. maxstringlen -= strlen(stringptr);
  255. stringptr += strlen(stringptr);
  256. snprintf(stringptr, maxstringlen, ";");
  257. maxstringlen -= strlen(stringptr);
  258. stringptr += strlen(stringptr);
  259. }
  260. }
  261. /* packet = parsed and in ascii in shared region -> copy to clients */
  262. snprintf(stringptr, maxstringlen, "\n");
  263. stringptr += strlen(stringptr);
  264. shared_setlen((stringptr - stringbuf));
  265. debug("Churnbuffer used: %d", (stringptr - stringbuf));
  266. master_permitread();
  267. }
  268. /* NOT REACHED */
  269. return (EX_SOFTWARE);
  270. }