| 1 | /* |
|---|
| 2 | * sensord |
|---|
| 3 | * |
|---|
| 4 | * A daemon that periodically logs sensor information to syslog. |
|---|
| 5 | * |
|---|
| 6 | * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org> |
|---|
| 7 | * |
|---|
| 8 | * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | * it under the terms of the GNU General Public License as published by |
|---|
| 10 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | * (at your option) any later version. |
|---|
| 12 | * |
|---|
| 13 | * This program is distributed in the hope that it will be useful, |
|---|
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | * GNU General Public License for more details. |
|---|
| 17 | * |
|---|
| 18 | * You should have received a copy of the GNU General Public License |
|---|
| 19 | * along with this program; if not, write to the Free Software |
|---|
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|---|
| 21 | * MA 02110-1301 USA. |
|---|
| 22 | */ |
|---|
| 23 | |
|---|
| 24 | #include <stdio.h> |
|---|
| 25 | #include <stdlib.h> |
|---|
| 26 | #include <string.h> |
|---|
| 27 | #include <getopt.h> |
|---|
| 28 | #include <syslog.h> |
|---|
| 29 | |
|---|
| 30 | #include "sensord.h" |
|---|
| 31 | #include "lib/error.h" |
|---|
| 32 | #include "version.h" |
|---|
| 33 | |
|---|
| 34 | #define MAX_CHIP_NAMES 32 |
|---|
| 35 | |
|---|
| 36 | int isDaemon = 0; |
|---|
| 37 | const char *sensorsCfgFile = NULL; |
|---|
| 38 | const char *pidFile = "/var/run/sensord.pid"; |
|---|
| 39 | const char *rrdFile = NULL; |
|---|
| 40 | const char *cgiDir = NULL; |
|---|
| 41 | int scanTime = 60; |
|---|
| 42 | int logTime = 30 * 60; |
|---|
| 43 | int rrdTime = 5 * 60; |
|---|
| 44 | int rrdNoAverage = 0; |
|---|
| 45 | int syslogFacility = LOG_LOCAL4; |
|---|
| 46 | int doScan = 0; |
|---|
| 47 | int doSet = 0; |
|---|
| 48 | int doCGI = 0; |
|---|
| 49 | int doLoad = 0; |
|---|
| 50 | int debug = 0; |
|---|
| 51 | sensors_chip_name chipNames[MAX_CHIP_NAMES]; |
|---|
| 52 | int numChipNames = 0; |
|---|
| 53 | |
|---|
| 54 | static int parseTime(char *arg) |
|---|
| 55 | { |
|---|
| 56 | char *end; |
|---|
| 57 | int value = strtoul(arg, &end, 10); |
|---|
| 58 | if ((end > arg) && (*end == 's')) { |
|---|
| 59 | ++ end; |
|---|
| 60 | } else if ((end > arg) && (*end == 'm')) { |
|---|
| 61 | value *= 60; |
|---|
| 62 | ++ end; |
|---|
| 63 | } else if ((end > arg) && (*end == 'h')) { |
|---|
| 64 | value *= 60 * 60; |
|---|
| 65 | ++ end; |
|---|
| 66 | } |
|---|
| 67 | if ((end == arg) || *end) { |
|---|
| 68 | fprintf(stderr, "Error parsing time value `%s'.\n", arg); |
|---|
| 69 | return -1; |
|---|
| 70 | } |
|---|
| 71 | return value; |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | static struct { |
|---|
| 75 | const char *name; |
|---|
| 76 | int id; |
|---|
| 77 | } facilities[] = { |
|---|
| 78 | { "local0", LOG_LOCAL0 }, |
|---|
| 79 | { "local1", LOG_LOCAL1 }, |
|---|
| 80 | { "local2", LOG_LOCAL2 }, |
|---|
| 81 | { "local3", LOG_LOCAL3 }, |
|---|
| 82 | { "local4", LOG_LOCAL4 }, |
|---|
| 83 | { "local5", LOG_LOCAL5 }, |
|---|
| 84 | { "local6", LOG_LOCAL6 }, |
|---|
| 85 | { "local7", LOG_LOCAL7 }, |
|---|
| 86 | { "daemon", LOG_DAEMON }, |
|---|
| 87 | { "user", LOG_USER }, |
|---|
| 88 | { NULL, 0 } |
|---|
| 89 | }; |
|---|
| 90 | |
|---|
| 91 | static int parseFacility(char *arg) |
|---|
| 92 | { |
|---|
| 93 | int i = 0; |
|---|
| 94 | while (facilities[i].name && strcasecmp(arg, facilities[i].name)) |
|---|
| 95 | ++ i; |
|---|
| 96 | if (!facilities[i].name) { |
|---|
| 97 | fprintf(stderr, "Error parsing facility value `%s'.\n", arg); |
|---|
| 98 | return -1; |
|---|
| 99 | } |
|---|
| 100 | return facilities[i].id; |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | static const char *daemonSyntax = |
|---|
| 104 | " -i, --interval <time> -- interval between scanning alarms (default 60s)\n" |
|---|
| 105 | " -l, --log-interval <time> -- interval between logging sensors (default 30m)\n" |
|---|
| 106 | " -t, --rrd-interval <time> -- interval between updating RRD file (default 5m)\n" |
|---|
| 107 | " -T, --rrd-no-average -- switch RRD in non-average mode\n" |
|---|
| 108 | " -r, --rrd-file <file> -- RRD file (default <none>)\n" |
|---|
| 109 | " -c, --config-file <file> -- configuration file\n" |
|---|
| 110 | " -p, --pid-file <file> -- PID file (default /var/run/sensord.pid)\n" |
|---|
| 111 | " -f, --syslog-facility <f> -- syslog facility to use (default local4)\n" |
|---|
| 112 | " -g, --rrd-cgi <img-dir> -- output an RRD CGI script and exit\n" |
|---|
| 113 | " -a, --load-average -- include load average in RRD file\n" |
|---|
| 114 | " -d, --debug -- display some debug information\n" |
|---|
| 115 | " -v, --version -- display version and exit\n" |
|---|
| 116 | " -h, --help -- display help and exit\n" |
|---|
| 117 | "\n" |
|---|
| 118 | "Specify a value of 0 for any interval to disable that operation;\n" |
|---|
| 119 | "for example, specify --log-interval 0 to only scan for alarms." |
|---|
| 120 | "\n" |
|---|
| 121 | "Specify the filename `-' to read the config file from stdin.\n" |
|---|
| 122 | "\n" |
|---|
| 123 | "If no chips are specified, all chip info will be printed.\n" |
|---|
| 124 | "\n" |
|---|
| 125 | "If unspecified, no RRD (round robin database) is used. If specified and the\n" |
|---|
| 126 | "file does not exist, it will be created. For RRD updates to be successful,\n" |
|---|
| 127 | "the RRD file configuration must EXACTLY match the sensors that are used. If\n" |
|---|
| 128 | "your configuration changes, delete the old RRD file and restart sensord.\n"; |
|---|
| 129 | |
|---|
| 130 | static const char *appSyntax = |
|---|
| 131 | " -a, --alarm-scan -- only scan for alarms\n" |
|---|
| 132 | " -s, --set -- execute set statements (root only)\n" |
|---|
| 133 | " -r, --rrd-file <file> -- only update RRD file\n" |
|---|
| 134 | " -c, --config-file <file> -- configuration file\n" |
|---|
| 135 | " -d, --debug -- display some debug information\n" |
|---|
| 136 | " -v, --version -- display version and exit\n" |
|---|
| 137 | " -h, --help -- display help and exit\n" |
|---|
| 138 | "\n" |
|---|
| 139 | "Specify the filename `-' to read the config file from stdin.\n" |
|---|
| 140 | "\n" |
|---|
| 141 | "If no chips are specified, all chip info will be printed.\n"; |
|---|
| 142 | |
|---|
| 143 | static const char *daemonShortOptions = "i:l:t:Tf:r:c:p:advhg:"; |
|---|
| 144 | |
|---|
| 145 | static const struct option daemonLongOptions[] = { |
|---|
| 146 | { "interval", required_argument, NULL, 'i' }, |
|---|
| 147 | { "log-interval", required_argument, NULL, 'l' }, |
|---|
| 148 | { "rrd-interval", required_argument, NULL, 't' }, |
|---|
| 149 | { "rrd-no-average", no_argument, NULL, 'T' }, |
|---|
| 150 | { "syslog-facility", required_argument, NULL, 'f' }, |
|---|
| 151 | { "rrd-file", required_argument, NULL, 'r' }, |
|---|
| 152 | { "config-file", required_argument, NULL, 'c' }, |
|---|
| 153 | { "pid-file", required_argument, NULL, 'p' }, |
|---|
| 154 | { "rrd-cgi", required_argument, NULL, 'g' }, |
|---|
| 155 | { "load-average", no_argument, NULL, 'a' }, |
|---|
| 156 | { "debug", no_argument, NULL, 'd' }, |
|---|
| 157 | { "version", no_argument, NULL, 'v' }, |
|---|
| 158 | { "help", no_argument, NULL, 'h' }, |
|---|
| 159 | { NULL, 0, NULL, 0 } |
|---|
| 160 | }; |
|---|
| 161 | |
|---|
| 162 | static const char *appShortOptions = "asr:c:dvh"; |
|---|
| 163 | |
|---|
| 164 | static const struct option appLongOptions[] = { |
|---|
| 165 | { "alarm-scan", no_argument, NULL, 'a' }, |
|---|
| 166 | { "set", no_argument, NULL, 's' }, |
|---|
| 167 | { "rrd-file", required_argument, NULL, 'r' }, |
|---|
| 168 | { "config-file", required_argument, NULL, 'c' }, |
|---|
| 169 | { "debug", no_argument, NULL, 'd' }, |
|---|
| 170 | { "version", no_argument, NULL, 'v' }, |
|---|
| 171 | { "help", no_argument, NULL, 'h' }, |
|---|
| 172 | { NULL, 0, NULL, 0 } |
|---|
| 173 | }; |
|---|
| 174 | |
|---|
| 175 | int parseArgs(int argc, char **argv) |
|---|
| 176 | { |
|---|
| 177 | int c; |
|---|
| 178 | const char *shortOptions; |
|---|
| 179 | const struct option *longOptions; |
|---|
| 180 | |
|---|
| 181 | isDaemon = (argv[0][strlen (argv[0]) - 1] == 'd'); |
|---|
| 182 | shortOptions = isDaemon ? daemonShortOptions : appShortOptions; |
|---|
| 183 | longOptions = isDaemon ? daemonLongOptions : appLongOptions; |
|---|
| 184 | |
|---|
| 185 | while ((c = getopt_long(argc, argv, shortOptions, longOptions, NULL)) |
|---|
| 186 | != EOF) { |
|---|
| 187 | switch(c) { |
|---|
| 188 | case 'i': |
|---|
| 189 | if ((scanTime = parseTime(optarg)) < 0) |
|---|
| 190 | return -1; |
|---|
| 191 | break; |
|---|
| 192 | case 'l': |
|---|
| 193 | if ((logTime = parseTime(optarg)) < 0) |
|---|
| 194 | return -1; |
|---|
| 195 | break; |
|---|
| 196 | case 't': |
|---|
| 197 | if ((rrdTime = parseTime(optarg)) < 0) |
|---|
| 198 | return -1; |
|---|
| 199 | break; |
|---|
| 200 | case 'T': |
|---|
| 201 | rrdNoAverage = 1; |
|---|
| 202 | break; |
|---|
| 203 | case 'f': |
|---|
| 204 | if ((syslogFacility = parseFacility(optarg)) < 0) |
|---|
| 205 | return -1; |
|---|
| 206 | break; |
|---|
| 207 | case 'a': |
|---|
| 208 | if (isDaemon) |
|---|
| 209 | doLoad = 1; |
|---|
| 210 | else |
|---|
| 211 | doScan = 1; |
|---|
| 212 | break; |
|---|
| 213 | case 's': |
|---|
| 214 | doSet = 1; |
|---|
| 215 | break; |
|---|
| 216 | case 'c': |
|---|
| 217 | sensorsCfgFile = optarg; |
|---|
| 218 | break; |
|---|
| 219 | case 'p': |
|---|
| 220 | pidFile = optarg; |
|---|
| 221 | break; |
|---|
| 222 | case 'r': |
|---|
| 223 | rrdFile = optarg; |
|---|
| 224 | break; |
|---|
| 225 | case 'd': |
|---|
| 226 | debug = 1; |
|---|
| 227 | break; |
|---|
| 228 | case 'g': |
|---|
| 229 | doCGI = 1; |
|---|
| 230 | cgiDir = optarg; |
|---|
| 231 | break; |
|---|
| 232 | case 'v': |
|---|
| 233 | printf("sensord version %s\n", LM_VERSION); |
|---|
| 234 | exit(EXIT_SUCCESS); |
|---|
| 235 | break; |
|---|
| 236 | case 'h': |
|---|
| 237 | printf("Syntax: %s {options} {chips}\n%s", argv[0], |
|---|
| 238 | isDaemon ? daemonSyntax : appSyntax); |
|---|
| 239 | exit(EXIT_SUCCESS); |
|---|
| 240 | break; |
|---|
| 241 | case ':': |
|---|
| 242 | case '?': |
|---|
| 243 | printf("Try `%s --help' for more information.\n", |
|---|
| 244 | argv[0]); |
|---|
| 245 | return -1; |
|---|
| 246 | break; |
|---|
| 247 | default: |
|---|
| 248 | fprintf(stderr, |
|---|
| 249 | "Internal error while parsing options.\n"); |
|---|
| 250 | return -1; |
|---|
| 251 | break; |
|---|
| 252 | } |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | if (doScan && doSet) { |
|---|
| 256 | fprintf(stderr, |
|---|
| 257 | "Error: Incompatible --set and --alarm-scan.\n"); |
|---|
| 258 | return -1; |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | if (rrdFile && doSet) { |
|---|
| 262 | fprintf(stderr, |
|---|
| 263 | "Error: Incompatible --set and --rrd-file.\n"); |
|---|
| 264 | return -1; |
|---|
| 265 | } |
|---|
| 266 | |
|---|
| 267 | if (doScan && rrdFile) { |
|---|
| 268 | fprintf(stderr, |
|---|
| 269 | "Error: Incompatible --rrd-file and --alarm-scan.\n"); |
|---|
| 270 | return -1; |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | if (doCGI && !rrdFile) { |
|---|
| 274 | fprintf(stderr, |
|---|
| 275 | "Error: Incompatible --rrd-cgi without --rrd-file.\n"); |
|---|
| 276 | return -1; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | if (rrdFile && !rrdTime) { |
|---|
| 280 | fprintf(stderr, |
|---|
| 281 | "Error: Incompatible --rrd-file without --rrd-interval.\n"); |
|---|
| 282 | return -1; |
|---|
| 283 | } |
|---|
| 284 | |
|---|
| 285 | if (!logTime && !scanTime && !rrdFile) { |
|---|
| 286 | fprintf(stderr, |
|---|
| 287 | "Error: No logging, alarm or RRD scanning.\n"); |
|---|
| 288 | return -1; |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | return 0; |
|---|
| 292 | } |
|---|
| 293 | |
|---|
| 294 | int parseChips(int argc, char **argv) |
|---|
| 295 | { |
|---|
| 296 | if (optind == argc) { |
|---|
| 297 | chipNames[0].prefix = SENSORS_CHIP_NAME_PREFIX_ANY; |
|---|
| 298 | chipNames[0].bus.type = SENSORS_BUS_TYPE_ANY; |
|---|
| 299 | chipNames[0].bus.nr = SENSORS_BUS_NR_ANY; |
|---|
| 300 | chipNames[0].addr = SENSORS_CHIP_NAME_ADDR_ANY; |
|---|
| 301 | numChipNames = 1; |
|---|
| 302 | } else { |
|---|
| 303 | int i, n = argc - optind, err; |
|---|
| 304 | if (n > MAX_CHIP_NAMES) { |
|---|
| 305 | fprintf(stderr, "Too many chip names.\n"); |
|---|
| 306 | return -1; |
|---|
| 307 | } |
|---|
| 308 | for (i = 0; i < n; ++ i) { |
|---|
| 309 | char *arg = argv[optind + i]; |
|---|
| 310 | if ((err = sensors_parse_chip_name(arg, |
|---|
| 311 | chipNames + i))) { |
|---|
| 312 | fprintf(stderr, |
|---|
| 313 | "Invalid chip name `%s': %s\n", arg, |
|---|
| 314 | sensors_strerror(err)); |
|---|
| 315 | return -1; |
|---|
| 316 | } |
|---|
| 317 | } |
|---|
| 318 | numChipNames = n; |
|---|
| 319 | } |
|---|
| 320 | return 0; |
|---|
| 321 | } |
|---|