symon.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. * Copyright (c) 2001-2010 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/param.h>
  31. #include <sys/time.h>
  32. #include <errno.h>
  33. #include <fcntl.h>
  34. #include <limits.h>
  35. #include <pwd.h>
  36. #include <signal.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <sysexits.h>
  41. #include <syslog.h>
  42. #include <time.h>
  43. #include <unistd.h>
  44. #include "conf.h"
  45. #include "data.h"
  46. #include "error.h"
  47. #include "net.h"
  48. #include "readconf.h"
  49. #include "symon.h"
  50. #include "symonnet.h"
  51. #include "xmalloc.h"
  52. __BEGIN_DECLS
  53. void alarmhandler(int);
  54. void drop_privileges();
  55. void exithandler(int);
  56. void huphandler(int);
  57. void init_streams(struct muxlist *mul);
  58. void drop_privileges(int unsecure);
  59. __END_DECLS
  60. int flag_unsecure = 0;
  61. int flag_hup = 0;
  62. int flag_testconf = 0;
  63. int symon_interval = 0;
  64. /* program wide time_t indicating start of measurement time */
  65. time_t now;
  66. /* map stream types to inits and getters */
  67. struct funcmap streamfunc[] = {
  68. {MT_IO1, 0, NULL, init_io, gets_io, get_io},
  69. {MT_CPU, 0, NULL, init_cpu, gets_cpu, get_cpu},
  70. {MT_MEM1, 0, NULL, init_mem, gets_mem, get_mem},
  71. {MT_IF1, 0, NULL, init_if, gets_if, get_if},
  72. {MT_PF, 0, privinit_pf, init_pf, gets_pf, get_pf},
  73. {MT_DEBUG, 0, NULL, init_debug, NULL, get_debug},
  74. {MT_PROC, 0, privinit_proc, init_proc, gets_proc, get_proc},
  75. {MT_MBUF, 0, NULL, init_mbuf, NULL, get_mbuf},
  76. {MT_SENSOR, 0, privinit_sensor, init_sensor, NULL, get_sensor},
  77. {MT_IO2, 0, NULL, init_io, gets_io, get_io},
  78. {MT_PFQ, 0, privinit_pfq, init_pfq, gets_pfq, get_pfq},
  79. {MT_DF, 0, NULL, init_df, gets_df, get_df},
  80. {MT_MEM2, 0, NULL, init_mem, gets_mem, get_mem},
  81. {MT_IF2, 0, NULL, init_if, gets_if, get_if},
  82. {MT_CPUIOW, 0, NULL, init_cpuiow, gets_cpuiow, get_cpuiow},
  83. {MT_SMART, 0, NULL, init_smart, gets_smart, get_smart},
  84. {MT_LOAD, 0, NULL, init_load, gets_load, get_load},
  85. {MT_FLUKSO, 0, NULL, init_flukso, gets_flukso, get_flukso},
  86. {MT_EOT, 0, NULL, NULL, NULL}
  87. };
  88. void
  89. init_streams(struct muxlist *mul)
  90. {
  91. struct itimerval alarminterval;
  92. struct stream *stream;
  93. struct mux *mux;
  94. mux = NULL;
  95. if ((mul == NULL) || ((mux = SLIST_FIRST(mul)) == NULL))
  96. fatal("empty mux list");
  97. symon_interval = mux->interval;
  98. SLIST_FOREACH(mux, mul, muxes) {
  99. /* determine gcd of alarm times */
  100. symon_interval = gcd(symon_interval, mux->interval);
  101. /* init network */
  102. init_symon_packet(mux);
  103. connect2mux(mux);
  104. /* init modules */
  105. SLIST_FOREACH(stream, &mux->sl, streams) {
  106. (streamfunc[stream->type].init) (stream);
  107. }
  108. }
  109. /* setup alarm */
  110. timerclear(&alarminterval.it_interval);
  111. timerclear(&alarminterval.it_value);
  112. alarminterval.it_interval.tv_sec =
  113. alarminterval.it_value.tv_sec = symon_interval;
  114. if (setitimer(ITIMER_REAL, &alarminterval, NULL) != 0) {
  115. fatal("alarm setup failed: %.200s", strerror(errno));
  116. }
  117. }
  118. void
  119. drop_privileges(int unsecure)
  120. {
  121. struct passwd *pw;
  122. if (unsecure) {
  123. if (setegid(getgid()) || setgid(getgid()) ||
  124. seteuid(getuid()) || setuid(getuid()))
  125. fatal("can't drop privileges: %.200s", strerror(errno));
  126. } else {
  127. if ((pw = getpwnam(SYMON_USER)) == NULL)
  128. fatal("could not get user information for user '%.200s': %.200s",
  129. SYMON_USER, strerror(errno));
  130. if (chroot(pw->pw_dir) < 0)
  131. fatal("chroot failed: %.200s", strerror(errno));
  132. if (chdir("/") < 0)
  133. fatal("chdir / failed: %.200s", strerror(errno));
  134. if (setgroups(1, &pw->pw_gid))
  135. fatal("can't setgroups: %.200s", strerror(errno));
  136. if (setgid(pw->pw_gid))
  137. fatal("can't set group id: %.200s", strerror(errno));
  138. if (setegid(pw->pw_gid))
  139. fatal("can't set effective group id: %.200s", strerror(errno));
  140. if (setuid(pw->pw_uid))
  141. fatal("can't set user id: %.200s", strerror(errno));
  142. if (seteuid(pw->pw_uid))
  143. fatal("can't set effective user id: %.200s", strerror(errno));
  144. }
  145. }
  146. /* alarmhandler that gets called every symon_interval */
  147. void
  148. alarmhandler(int s)
  149. {
  150. /* EMPTY */
  151. }
  152. void
  153. exithandler(int s)
  154. {
  155. info("received signal %d - quitting", s);
  156. exit(1);
  157. }
  158. void
  159. huphandler(int s)
  160. {
  161. info("hup received");
  162. flag_hup = 1;
  163. }
  164. /*
  165. * Symon is a system measurement utility.
  166. *
  167. * The main goals symon hopes to accomplish are:
  168. * - to take fine grained measurements of system parameters
  169. * - with minimal performance impact
  170. * - in a secure way.
  171. *
  172. * Measurements are processed by a second program called symux. symon and symux
  173. * communicate via udp.
  174. */
  175. int
  176. main(int argc, char *argv[])
  177. {
  178. struct muxlist mul, newmul;
  179. struct stream *stream;
  180. struct mux *mux;
  181. time_t last_update;
  182. FILE *pidfile;
  183. char *cfgpath;
  184. int ch;
  185. int i;
  186. SLIST_INIT(&mul);
  187. /* reset flags */
  188. flag_debug = 0;
  189. flag_daemon = 0;
  190. flag_unsecure = 0;
  191. cfgpath = SYMON_CONFIG_FILE;
  192. while ((ch = getopt(argc, argv, "df:tuv")) != -1) {
  193. switch (ch) {
  194. case 'd':
  195. flag_debug = 1;
  196. break;
  197. case 'f':
  198. cfgpath = xstrdup(optarg);
  199. break;
  200. case 't':
  201. flag_testconf = 1;
  202. break;
  203. case 'u':
  204. flag_unsecure = 1;
  205. break;
  206. case 'v':
  207. info("symon version %s", SYMON_VERSION);
  208. default:
  209. info("usage: %s [-d] [-t] [-u] [-v] [-f cfgfile]", __progname);
  210. exit(EX_USAGE);
  211. }
  212. }
  213. if (!read_config_file(&mul, cfgpath))
  214. fatal("configuration file contained errors - aborting");
  215. if (flag_testconf) {
  216. info("%s: ok", cfgpath);
  217. exit(EX_OK);
  218. }
  219. SLIST_FOREACH(mux, &mul, muxes) {
  220. SLIST_FOREACH(stream, &mux->sl, streams) {
  221. streamfunc[stream->type].used = 1;
  222. }
  223. }
  224. /* open resources that might not be available after privilege drop */
  225. for (i = 0; i < MT_EOT; i++)
  226. if (streamfunc[i].used && (streamfunc[i].privinit != NULL))
  227. (streamfunc[i].privinit) ();
  228. if ((pidfile = fopen(SYMON_PID_FILE, "w")) == NULL)
  229. warning("could not open \"%.200s\", %.200s", SYMON_PID_FILE,
  230. strerror(errno));
  231. if (flag_debug != 1) {
  232. if (daemon(0, 0) != 0)
  233. fatal("daemonize failed: %.200s", strerror(errno));
  234. flag_daemon = 1;
  235. if (pidfile) {
  236. fprintf(pidfile, "%u\n", (u_int) getpid());
  237. fclose(pidfile);
  238. }
  239. }
  240. drop_privileges(flag_unsecure);
  241. /* TODO: howto log using active timezone, which may be unavailable in
  242. * chroot */
  243. info("symon version %s", SYMON_VERSION);
  244. if (flag_debug == 1)
  245. info("program id=%d", (u_int) getpid());
  246. /* setup signal handlers */
  247. signal(SIGALRM, alarmhandler);
  248. signal(SIGHUP, huphandler);
  249. signal(SIGINT, exithandler);
  250. signal(SIGQUIT, exithandler);
  251. signal(SIGTERM, exithandler);
  252. /* prepare crc32 */
  253. init_crc32();
  254. init_streams(&mul);
  255. last_update = time(NULL);
  256. for (;;) { /* FOREVER */
  257. sleep(symon_interval * 2); /* alarm will interrupt sleep */
  258. now = time(NULL);
  259. if (flag_hup == 1) {
  260. flag_hup = 0;
  261. SLIST_INIT(&newmul);
  262. if (flag_unsecure) {
  263. if (!read_config_file(&newmul, cfgpath)) {
  264. info("new configuration contains errors; keeping old configuration");
  265. free_muxlist(&newmul);
  266. } else {
  267. free_muxlist(&mul);
  268. mul = newmul;
  269. info("read configuration file '%.200s' successfully", cfgpath);
  270. /* init modules */
  271. init_streams(&mul);
  272. }
  273. } else {
  274. info("configuration unreachable because of privsep; keeping old configuration");
  275. }
  276. } else {
  277. /* check timing to catch ntp drifts */
  278. if (now < last_update ||
  279. now > last_update + symon_interval + symon_interval) {
  280. info("last update seems long ago - assuming system time change");
  281. last_update = now;
  282. } else if (now < last_update + symon_interval) {
  283. debug("did not sleep %d seconds - skipping a measurement", symon_interval);
  284. continue;
  285. }
  286. last_update = now;
  287. /* populate for modules that get all their measurements in one
  288. * go. we bunch up calls together to ensure that the measurements
  289. * happen "at the same time" */
  290. for (i = 0; i < MT_EOT; i++)
  291. streamfunc[i].used = 0;
  292. SLIST_FOREACH(mux, &mul, muxes) {
  293. mux->last += symon_interval;
  294. if (mux->last >= mux->interval) {
  295. SLIST_FOREACH(stream, &mux->sl, streams) {
  296. if (streamfunc[stream->type].used == 0) {
  297. streamfunc[stream->type].used = 1;
  298. if (streamfunc[stream->type].gets != NULL)
  299. (streamfunc[stream->type].gets)();
  300. }
  301. }
  302. }
  303. }
  304. SLIST_FOREACH(mux, &mul, muxes) {
  305. if (mux->last >= mux->interval) {
  306. mux->last = 0;
  307. prepare_packet(mux, now);
  308. SLIST_FOREACH(stream, &mux->sl, streams)
  309. stream_in_packet(stream, mux);
  310. finish_packet(mux);
  311. send_packet(mux);
  312. }
  313. }
  314. }
  315. }
  316. return (EX_SOFTWARE); /* NOTREACHED */
  317. }