| 1 | /* |
|---|
| 2 | main.c - Part of sensors, a user-space program for hardware monitoring |
|---|
| 3 | Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> |
|---|
| 4 | Copyright (C) 2007 Jean Delvare <khali@linux-fr.org> |
|---|
| 5 | |
|---|
| 6 | This program is free software; you can redistribute it and/or modify |
|---|
| 7 | it under the terms of the GNU General Public License as published by |
|---|
| 8 | the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | This program is distributed in the hope that it will be useful, |
|---|
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | GNU General Public License for more details. |
|---|
| 15 | |
|---|
| 16 | You should have received a copy of the GNU General Public License |
|---|
| 17 | along with this program; if not, write to the Free Software |
|---|
| 18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 19 | */ |
|---|
| 20 | |
|---|
| 21 | #include <stdio.h> |
|---|
| 22 | #include <stdlib.h> |
|---|
| 23 | #include <getopt.h> |
|---|
| 24 | #include <string.h> |
|---|
| 25 | #include <errno.h> |
|---|
| 26 | #include <locale.h> |
|---|
| 27 | #include <langinfo.h> |
|---|
| 28 | |
|---|
| 29 | #ifndef __UCLIBC__ |
|---|
| 30 | #include <iconv.h> |
|---|
| 31 | #define HAVE_ICONV |
|---|
| 32 | #endif |
|---|
| 33 | |
|---|
| 34 | #include "lib/sensors.h" |
|---|
| 35 | #include "lib/error.h" |
|---|
| 36 | #include "main.h" |
|---|
| 37 | #include "chips.h" |
|---|
| 38 | #include "version.h" |
|---|
| 39 | |
|---|
| 40 | #define PROGRAM "sensors" |
|---|
| 41 | #define VERSION LM_VERSION |
|---|
| 42 | #define DEFAULT_CONFIG_FILE ETCDIR "/sensors.conf" |
|---|
| 43 | |
|---|
| 44 | static int do_sets, do_raw, hide_adapter; |
|---|
| 45 | |
|---|
| 46 | int fahrenheit; |
|---|
| 47 | char degstr[5]; /* store the correct string to print degrees */ |
|---|
| 48 | |
|---|
| 49 | static void print_short_help(void) |
|---|
| 50 | { |
|---|
| 51 | printf("Try `%s -h' for more information\n", PROGRAM); |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | static void print_long_help(void) |
|---|
| 55 | { |
|---|
| 56 | printf("Usage: %s [OPTION]... [CHIP]...\n", PROGRAM); |
|---|
| 57 | printf(" -c, --config-file Specify a config file (default: %s)\n", |
|---|
| 58 | DEFAULT_CONFIG_FILE); |
|---|
| 59 | puts(" -h, --help Display this help text\n" |
|---|
| 60 | " -s, --set Execute `set' statements (root only)\n" |
|---|
| 61 | " -f, --fahrenheit Show temperatures in degrees fahrenheit\n" |
|---|
| 62 | " -A, --no-adapter Do not show adapter for each chip\n" |
|---|
| 63 | " --bus-list Generate bus statements for sensors.conf\n" |
|---|
| 64 | " -u Raw output (debugging only)\n" |
|---|
| 65 | " -v, --version Display the program version\n" |
|---|
| 66 | "\n" |
|---|
| 67 | "Use `-' after `-c' to read the config file from stdin.\n" |
|---|
| 68 | "If no chips are specified, all chip info will be printed.\n" |
|---|
| 69 | "Example chip names:\n" |
|---|
| 70 | "\tlm78-i2c-0-2d\t*-i2c-0-2d\n" |
|---|
| 71 | "\tlm78-i2c-0-*\t*-i2c-0-*\n" |
|---|
| 72 | "\tlm78-i2c-*-2d\t*-i2c-*-2d\n" |
|---|
| 73 | "\tlm78-i2c-*-*\t*-i2c-*-*\n" |
|---|
| 74 | "\tlm78-isa-0290\t*-isa-0290\n" |
|---|
| 75 | "\tlm78-isa-*\t*-isa-*\n" |
|---|
| 76 | "\tlm78-*"); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | static void print_version(void) |
|---|
| 80 | { |
|---|
| 81 | printf("%s version %s with libsensors version %s\n", PROGRAM, VERSION, |
|---|
| 82 | libsensors_version); |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /* Return 0 on success, and an exit error code otherwise */ |
|---|
| 86 | static int read_config_file(const char *config_file_name) |
|---|
| 87 | { |
|---|
| 88 | FILE *config_file; |
|---|
| 89 | int err; |
|---|
| 90 | |
|---|
| 91 | if (!strcmp(config_file_name, "-")) |
|---|
| 92 | config_file = stdin; |
|---|
| 93 | else |
|---|
| 94 | config_file = fopen(config_file_name, "r"); |
|---|
| 95 | |
|---|
| 96 | if (!config_file) { |
|---|
| 97 | fprintf(stderr, "Could not open config file\n"); |
|---|
| 98 | perror(config_file_name); |
|---|
| 99 | return 1; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | err = sensors_init(config_file); |
|---|
| 103 | if (err) { |
|---|
| 104 | fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err)); |
|---|
| 105 | return 1; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | if (fclose(config_file) == EOF) |
|---|
| 109 | perror(config_file_name); |
|---|
| 110 | |
|---|
| 111 | return 0; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | static void set_degstr(void) |
|---|
| 115 | { |
|---|
| 116 | const char *deg_default_text[2] = { " C", " F" }; |
|---|
| 117 | |
|---|
| 118 | #ifdef HAVE_ICONV |
|---|
| 119 | /* Size hardcoded for better performance. |
|---|
| 120 | Don't forget to count the trailing \0! */ |
|---|
| 121 | size_t deg_latin1_size = 3; |
|---|
| 122 | char *deg_latin1_text[2] = { "\260C", "\260F" }; |
|---|
| 123 | size_t nconv; |
|---|
| 124 | size_t degstr_size = sizeof(degstr); |
|---|
| 125 | char *degstr_ptr = degstr; |
|---|
| 126 | |
|---|
| 127 | iconv_t cd = iconv_open(nl_langinfo(CODESET), "ISO-8859-1"); |
|---|
| 128 | if (cd != (iconv_t) -1) { |
|---|
| 129 | nconv = iconv(cd, &(deg_latin1_text[fahrenheit]), |
|---|
| 130 | °_latin1_size, °str_ptr, °str_size); |
|---|
| 131 | iconv_close(cd); |
|---|
| 132 | |
|---|
| 133 | if (nconv != (size_t) -1) |
|---|
| 134 | return; |
|---|
| 135 | } |
|---|
| 136 | #endif /* HAVE_ICONV */ |
|---|
| 137 | |
|---|
| 138 | /* There was an error during the conversion, use the default text */ |
|---|
| 139 | strcpy(degstr, deg_default_text[fahrenheit]); |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | static const char *sprintf_chip_name(const sensors_chip_name *name) |
|---|
| 143 | { |
|---|
| 144 | #define BUF_SIZE 200 |
|---|
| 145 | static char buf[BUF_SIZE]; |
|---|
| 146 | |
|---|
| 147 | if (sensors_snprintf_chip_name(buf, BUF_SIZE, name) < 0) |
|---|
| 148 | return NULL; |
|---|
| 149 | return buf; |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | static void do_a_print(const sensors_chip_name *name) |
|---|
| 153 | { |
|---|
| 154 | printf("%s\n", sprintf_chip_name(name)); |
|---|
| 155 | if (!hide_adapter) { |
|---|
| 156 | const char *adap = sensors_get_adapter_name(&name->bus); |
|---|
| 157 | if (adap) |
|---|
| 158 | printf("Adapter: %s\n", adap); |
|---|
| 159 | else |
|---|
| 160 | fprintf(stderr, "Can't get adapter name\n"); |
|---|
| 161 | } |
|---|
| 162 | if (do_raw) |
|---|
| 163 | print_chip_raw(name); |
|---|
| 164 | else |
|---|
| 165 | print_chip(name); |
|---|
| 166 | printf("\n"); |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | /* returns 1 on error */ |
|---|
| 170 | static int do_a_set(const sensors_chip_name *name) |
|---|
| 171 | { |
|---|
| 172 | int res; |
|---|
| 173 | |
|---|
| 174 | if ((res = sensors_do_chip_sets(name))) { |
|---|
| 175 | if (res == -SENSORS_ERR_KERNEL) { |
|---|
| 176 | fprintf(stderr, "%s: %s;\n", |
|---|
| 177 | sprintf_chip_name(name), |
|---|
| 178 | sensors_strerror(res)); |
|---|
| 179 | fprintf(stderr, "Run as root?\n"); |
|---|
| 180 | return 1; |
|---|
| 181 | } else if (res == -SENSORS_ERR_ACCESS_W) { |
|---|
| 182 | fprintf(stderr, |
|---|
| 183 | "%s: At least one \"set\" statement failed\n", |
|---|
| 184 | sprintf_chip_name(name)); |
|---|
| 185 | } else { |
|---|
| 186 | fprintf(stderr, "%s: %s\n", sprintf_chip_name(name), |
|---|
| 187 | sensors_strerror(res)); |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | return 0; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | /* returns number of chips found */ |
|---|
| 194 | static int do_the_real_work(const sensors_chip_name *match, int *error) |
|---|
| 195 | { |
|---|
| 196 | const sensors_chip_name *chip; |
|---|
| 197 | int chip_nr; |
|---|
| 198 | int cnt = 0; |
|---|
| 199 | |
|---|
| 200 | chip_nr = 0; |
|---|
| 201 | while ((chip = sensors_get_detected_chips(match, &chip_nr))) { |
|---|
| 202 | if (do_sets) { |
|---|
| 203 | if (do_a_set(chip)) |
|---|
| 204 | *error = 1; |
|---|
| 205 | } else |
|---|
| 206 | do_a_print(chip); |
|---|
| 207 | cnt++; |
|---|
| 208 | } |
|---|
| 209 | return cnt; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | /* List the buses in a format suitable for sensors.conf. We only list |
|---|
| 213 | bus types for which bus statements are actually useful and supported. |
|---|
| 214 | Known bug: i2c buses with number >= 32 or 64 could be listed several |
|---|
| 215 | times. Very unlikely to ever happen, though. */ |
|---|
| 216 | static void print_bus_list(void) |
|---|
| 217 | { |
|---|
| 218 | const sensors_chip_name *chip; |
|---|
| 219 | int chip_nr; |
|---|
| 220 | unsigned long seen_i2c = 0; |
|---|
| 221 | |
|---|
| 222 | chip_nr = 0; |
|---|
| 223 | while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) { |
|---|
| 224 | switch (chip->bus.type) { |
|---|
| 225 | case SENSORS_BUS_TYPE_I2C: |
|---|
| 226 | if (chip->bus.nr < (int)sizeof(unsigned long) * 8) { |
|---|
| 227 | if (seen_i2c & (1 << chip->bus.nr)) |
|---|
| 228 | break; |
|---|
| 229 | seen_i2c |= 1 << chip->bus.nr; |
|---|
| 230 | } |
|---|
| 231 | printf("bus \"i2c-%d\" \"%s\"\n", chip->bus.nr, |
|---|
| 232 | sensors_get_adapter_name(&chip->bus)); |
|---|
| 233 | break; |
|---|
| 234 | } |
|---|
| 235 | } |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | int main(int argc, char *argv[]) |
|---|
| 239 | { |
|---|
| 240 | int c, res, i, error, do_bus_list; |
|---|
| 241 | const char *config_file_name = DEFAULT_CONFIG_FILE; |
|---|
| 242 | |
|---|
| 243 | struct option long_opts[] = { |
|---|
| 244 | { "help", no_argument, NULL, 'h' }, |
|---|
| 245 | { "set", no_argument, NULL, 's' }, |
|---|
| 246 | { "version", no_argument, NULL, 'v'}, |
|---|
| 247 | { "fahrenheit", no_argument, NULL, 'f' }, |
|---|
| 248 | { "no-adapter", no_argument, NULL, 'A' }, |
|---|
| 249 | { "config-file", required_argument, NULL, 'c' }, |
|---|
| 250 | { "bus-list", no_argument, NULL, 'B' }, |
|---|
| 251 | { 0, 0, 0, 0 } |
|---|
| 252 | }; |
|---|
| 253 | |
|---|
| 254 | setlocale(LC_CTYPE, ""); |
|---|
| 255 | |
|---|
| 256 | do_raw = 0; |
|---|
| 257 | do_sets = 0; |
|---|
| 258 | do_bus_list = 0; |
|---|
| 259 | hide_adapter = 0; |
|---|
| 260 | while (1) { |
|---|
| 261 | c = getopt_long(argc, argv, "hsvfAc:u", long_opts, NULL); |
|---|
| 262 | if (c == EOF) |
|---|
| 263 | break; |
|---|
| 264 | switch(c) { |
|---|
| 265 | case ':': |
|---|
| 266 | case '?': |
|---|
| 267 | print_short_help(); |
|---|
| 268 | exit(1); |
|---|
| 269 | case 'h': |
|---|
| 270 | print_long_help(); |
|---|
| 271 | exit(0); |
|---|
| 272 | case 'v': |
|---|
| 273 | print_version(); |
|---|
| 274 | exit(0); |
|---|
| 275 | case 'c': |
|---|
| 276 | config_file_name = optarg; |
|---|
| 277 | break; |
|---|
| 278 | case 's': |
|---|
| 279 | do_sets = 1; |
|---|
| 280 | break; |
|---|
| 281 | case 'f': |
|---|
| 282 | fahrenheit = 1; |
|---|
| 283 | break; |
|---|
| 284 | case 'A': |
|---|
| 285 | hide_adapter = 1; |
|---|
| 286 | break; |
|---|
| 287 | case 'u': |
|---|
| 288 | do_raw = 1; |
|---|
| 289 | break; |
|---|
| 290 | case 'B': |
|---|
| 291 | do_bus_list = 1; |
|---|
| 292 | break; |
|---|
| 293 | default: |
|---|
| 294 | fprintf(stderr, |
|---|
| 295 | "Internal error while parsing options!\n"); |
|---|
| 296 | exit(1); |
|---|
| 297 | } |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | res = read_config_file(config_file_name); |
|---|
| 301 | if (res) |
|---|
| 302 | exit(res); |
|---|
| 303 | |
|---|
| 304 | /* build the degrees string */ |
|---|
| 305 | set_degstr(); |
|---|
| 306 | |
|---|
| 307 | if (do_bus_list) { |
|---|
| 308 | print_bus_list(); |
|---|
| 309 | } else if (optind == argc) { /* No chip name on command line */ |
|---|
| 310 | if (!do_the_real_work(NULL, &error)) { |
|---|
| 311 | fprintf(stderr, |
|---|
| 312 | "No sensors found!\n" |
|---|
| 313 | "Make sure you loaded all the kernel drivers you need.\n" |
|---|
| 314 | "Try sensors-detect to find out which these are.\n"); |
|---|
| 315 | error = 1; |
|---|
| 316 | } |
|---|
| 317 | } else { |
|---|
| 318 | int cnt = 0; |
|---|
| 319 | sensors_chip_name chip; |
|---|
| 320 | |
|---|
| 321 | for (i = optind; i < argc; i++) { |
|---|
| 322 | if (sensors_parse_chip_name(argv[i], &chip)) { |
|---|
| 323 | fprintf(stderr, |
|---|
| 324 | "Parse error in chip name `%s'\n", |
|---|
| 325 | argv[i]); |
|---|
| 326 | print_short_help(); |
|---|
| 327 | error = 1; |
|---|
| 328 | goto exit; |
|---|
| 329 | } |
|---|
| 330 | cnt += do_the_real_work(&chip, &error); |
|---|
| 331 | } |
|---|
| 332 | |
|---|
| 333 | if (!cnt) { |
|---|
| 334 | fprintf(stderr, "Specified sensor(s) not found!\n"); |
|---|
| 335 | error = 1; |
|---|
| 336 | } |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | exit: |
|---|
| 340 | sensors_cleanup(); |
|---|
| 341 | exit(res); |
|---|
| 342 | } |
|---|