/* Begin code stolen from bestups.c */ static void setup_serial(void) { struct termios tio; if (tcgetattr(upsfd, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcgetattr"); tio.c_iflag = IXON | IXOFF; tio.c_oflag = 0; tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); tio.c_lflag = 0; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; #ifdef HAVE_CFSETISPEED cfsetispeed(&tio, B1200); /* baud change here */ cfsetospeed(&tio, B1200); #else #error This system lacks cfsetispeed() and has no other means to set the speed #endif if (tcsetattr(upsfd, TCSANOW, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcsetattr"); /* end code stolen from bestups.c */ sync_serial(); }
/* close ttys and become a daemon */ void background(void) { int pid; if ((pid = fork()) < 0) fatal_with_errno(EXIT_FAILURE, "Unable to enter background"); xbit_set(&upslog_flags, UPSLOG_SYSLOG); xbit_clear(&upslog_flags, UPSLOG_STDERR); close(0); close(1); close(2); if (pid != 0) _exit(EXIT_SUCCESS); /* parent */ /* child */ /* make fds 0-2 point somewhere defined */ if (open("/dev/null", O_RDWR) != 0) fatal_with_errno(EXIT_FAILURE, "open /dev/null"); if (dup(0) == -1) fatal_with_errno(EXIT_FAILURE, "dup"); if (dup(0) == -1) fatal_with_errno(EXIT_FAILURE, "dup"); #ifdef HAVE_SETSID setsid(); /* make a new session to dodge signals */ #endif upslogx(LOG_INFO, "Startup successful"); }
static int open_sock(void) { int ret, fd; struct sockaddr_un ssaddr; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) fatal_with_errno(EXIT_FAILURE, "Can't create a unix domain socket"); ssaddr.sun_family = AF_UNIX; snprintf(ssaddr.sun_path, sizeof(ssaddr.sun_path), "%s", pipefn); unlink(pipefn); umask(0007); ret = bind(fd, (struct sockaddr *) &ssaddr, sizeof ssaddr); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "bind %s failed", pipefn); ret = chmod(pipefn, 0660); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "chmod(%s, 0660) failed", pipefn); ret = listen(fd, US_LISTEN_BACKLOG); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, US_LISTEN_BACKLOG); return fd; }
int upsdrv_initups(void) { struct termios tio; const char *val; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* * Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_iflag |= IGNCR; /* Ignore CR */ tio.c_iflag |= IGNBRK; /* Ignore break condition */ tio.c_oflag |= ONLCR; /* Map NL to CR-NL on output */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = _POSIX_VDISABLE; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } /* * Set DTR and clear RTS to provide power for the serial interface. */ ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); val = dstate_getinfo("battery.voltage.nominal"); battery.voltage.nom = (val) ? strtod(val, NULL) : 12.00; val = dstate_getinfo("battery.voltage.low"); battery.voltage.low = (val) ? strtod(val, NULL) : 10.80; if (battery.voltage.nom <= battery.voltage.low) { fatalx(EXIT_FAILURE, "Nominal battery voltage must be higher than low battery voltage!"); } return 1; }
/* drop down into a directory and throw away pointers to the old path */ void chroot_start(const char *path) { if (chdir(path)) fatal_with_errno(EXIT_FAILURE, "chdir(%s)", path); if (chroot(path)) fatal_with_errno(EXIT_FAILURE, "chroot(%s)", path); if (chdir("/")) fatal_with_errno(EXIT_FAILURE, "chdir(/)"); upsdebugx(1, "chrooted into %s", path); }
/*---------------------------------------------------------------------*/ void write_file( const char *irx, unsigned char *data, int size ) { FILE *f; if ( ( f = fopen( irx, "w" ) ) == NULL ) fatal_with_errno( "Cannot create file %s", irx ); if ( fwrite( data, size, 1, f ) != 1 ) fatal_with_errno( "Cannot write to file %s", irx ); if ( fclose( f ) == -1 ) fatal_with_errno( "Cannot close file %s", irx ); }
static void runparent(int fd) { int ret; char ch; /* handling signals is the child's job */ signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); ret = read(fd, &ch, 1); if (ret < 1) { if (errno == ENOENT) fatalx(EXIT_FAILURE, "upsmon parent: exiting (child exited)"); fatal_with_errno(EXIT_FAILURE, "upsmon parent: read"); } if (ch != 1) fatalx(EXIT_FAILURE, "upsmon parent: got bogus pipe command %c", ch); /* have to do this here - child is unprivileged */ set_pdflag(); ret = system(shutdowncmd); if (ret != 0) upslogx(LOG_ERR, "parent: Unable to call shutdown command: %s", shutdowncmd); close(fd); exit(EXIT_SUCCESS); }
static void us_serialize(int op) { static int pipefd[2]; int ret; char ch; switch(op) { case SERIALIZE_INIT: ret = pipe(pipefd); if (ret != 0) fatal_with_errno(EXIT_FAILURE, "serialize: pipe"); break; case SERIALIZE_SET: close(pipefd[0]); close(pipefd[1]); break; case SERIALIZE_WAIT: close(pipefd[1]); ret = read(pipefd[0], &ch, 1); close(pipefd[0]); break; } }
void *xcalloc(size_t number, size_t size) { void *p = calloc(number, size); if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); return p; }
void *xrealloc(void *ptr, size_t size) { void *p = realloc(ptr, size); if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); return p; }
char *xstrdup(const char *string) { char *p = strdup(string); if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); return p; }
static void start_driver(const ups_t *ups) { char *argv[8]; char dfn[SMALLBUF]; int ret, arg = 0; int initial_exec_error = exec_error, drv_maxretry = maxretry; struct stat fs; upsdebugx(1, "Starting UPS: %s", ups->upsname); snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); ret = stat(dfn, &fs); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "Can't start %s", dfn); argv[arg++] = dfn; argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; /* stick on the chroot / user args if given to us */ if (pt_root) { argv[arg++] = (char *)"-r"; /* FIXME: cast away const */ argv[arg++] = pt_root; } if (pt_user) { argv[arg++] = (char *)"-u"; /* FIXME: cast away const */ argv[arg++] = pt_user; } /* tie it off */ argv[arg++] = NULL; while (drv_maxretry > 0) { int cur_exec_error = exec_error; upsdebugx(2, "%i remaining attempts", drv_maxretry); debugcmdline(2, "exec: ", argv); drv_maxretry--; if (!testmode) { forkexec(argv, ups); } /* driver command succeeded */ if (cur_exec_error == exec_error) { drv_maxretry = 0; exec_error = initial_exec_error; } else { /* otherwise, retry if still needed */ if (drv_maxretry > 0) sleep (retrydelay); } } }
/* change to the user defined in the struct */ void become_user(struct passwd *pw) { /* if we can't switch users, then don't even try */ if ((geteuid() != 0) && (getuid() != 0)) return; if (getuid() == 0) if (seteuid(0)) fatal_with_errno(EXIT_FAILURE, "getuid gave 0, but seteuid(0) failed"); if (initgroups(pw->pw_name, pw->pw_gid) == -1) fatal_with_errno(EXIT_FAILURE, "initgroups"); if (setgid(pw->pw_gid) == -1) fatal_with_errno(EXIT_FAILURE, "setgid"); if (setuid(pw->pw_uid) == -1) fatal_with_errno(EXIT_FAILURE, "setuid"); }
static int sock_open(const char *fn) { int ret, fd; struct sockaddr_un ssaddr; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { fatal_with_errno(EXIT_FAILURE, "Can't create a unix domain socket"); } /* keep this around for the unlink() when exiting */ sockfn = xstrdup(fn); ssaddr.sun_family = AF_UNIX; snprintf(ssaddr.sun_path, sizeof(ssaddr.sun_path), "%s", sockfn); unlink(sockfn); /* group gets access so upsd can be a different user but same group */ umask(0007); ret = bind(fd, (struct sockaddr *) &ssaddr, sizeof ssaddr); if (ret < 0) { sock_fail(sockfn); } ret = chmod(sockfn, 0660); if (ret < 0) { fatal_with_errno(EXIT_FAILURE, "chmod(%s, 0660) failed", sockfn); } ret = listen(fd, DS_LISTEN_BACKLOG); if (ret < 0) { fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, DS_LISTEN_BACKLOG); } return fd; }
/* handlers: reload on HUP, exit on INT/QUIT/TERM */ static void setup_signals(void) { struct sigaction sa; sigemptyset(&nut_upslog_sigmask); sigaddset(&nut_upslog_sigmask, SIGHUP); sa.sa_mask = nut_upslog_sigmask; sa.sa_handler = set_reopen_flag; sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGHUP handler"); sa.sa_handler = set_exit_flag; if (sigaction(SIGINT, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGINT handler"); if (sigaction(SIGQUIT, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGQUIT handler"); if (sigaction(SIGTERM, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGTERM handler"); }
static void reopen_log(void) { if (logfile == stdout) { upslogx(LOG_INFO, "logging to stdout"); return; } fclose(logfile); logfile = fopen(logfn, "a"); if (logfile == NULL) fatal_with_errno(EXIT_FAILURE, "could not reopen logfile %s", logfn); }
/* this may be a frequent stumbling point for new users, so be verbose here */ static void sock_fail(const char *fn) { int sockerr; struct passwd *user; /* save this so it doesn't get overwritten */ sockerr = errno; /* dispense with the usual upslog stuff since we have stderr here */ printf("\nFatal error: unable to create listener socket\n\n"); printf("bind %s failed: %s\n", fn, strerror(sockerr)); user = getpwuid(getuid()); if (!user) { fatal_with_errno(EXIT_FAILURE, "getpwuid"); } /* deal with some common problems */ switch (errno) { case EACCES: printf("\nCurrent user: %s (UID %d)\n\n", user->pw_name, (int)user->pw_uid); printf("Things to try:\n\n"); printf(" - set different owners or permissions on %s\n\n", dflt_statepath()); printf(" - run this as some other user " "(try -u <username>)\n"); break; case ENOENT: printf("\nThings to try:\n\n"); printf(" - mkdir %s\n", dflt_statepath()); break; case ENOTDIR: printf("\nThings to try:\n\n"); printf(" - rm %s\n\n", dflt_statepath()); printf(" - mkdir %s\n", dflt_statepath()); break; } /* * there - that wasn't so bad. every helpful line of code here * prevents one more "help me" mail to the list a year from now */ printf("\n"); fatalx(EXIT_FAILURE, "Exiting."); }
void upsdrv_initinfo(void) { if (ivt_status() < 7) { fatal_with_errno(EXIT_FAILURE, "IVT Solar Controller not detected"); } /* set the device general information */ dstate_setinfo("device.mfr", "IVT"); dstate_setinfo("device.model", "Solar Controller Device"); dstate_setinfo("device.type", "scd"); dstate_addcmd("reset.input.minmax"); upsh.instcmd = instcmd; }
/* fire up the split parent/child scheme */ static void start_pipe(void) { int ret; ret = pipe(pipefd); if (ret) fatal_with_errno(EXIT_FAILURE, "pipe creation failed"); ret = fork(); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "fork failed"); /* start the privileged parent */ if (ret != 0) { close(pipefd[1]); runparent(pipefd[0]); exit(EXIT_FAILURE); /* NOTREACHED */ } close(pipefd[0]); }
/* do this here to keep pwd/grp stuff out of the main files */ struct passwd *get_user_pwent(const char *name) { struct passwd *r; errno = 0; if ((r = getpwnam(name))) return r; /* POSIX does not specify that "user not found" is an error, so some implementations of getpwnam() do not set errno when this happens. */ if (errno == 0) fatalx(EXIT_FAILURE, "user %s not found", name); else fatal_with_errno(EXIT_FAILURE, "getpwnam(%s)", name); return NULL; /* to make the compiler happy */ }
/* Attempt to retrieve a monotonic clock, do not trust this though, it may lie on various platforms! */ void clock_monotonic(struct timespec *t) { static int monotonic_works = 1; int ret; if (monotonic_works) { ret = clock_gettime(CLOCK_MONOTONIC, t); if (ret != 0) { if (errno == EINVAL) { upslogx(LOG_WARNING, "Monotonic clock is not supported on this platform"); monotonic_works = 0; } else { fatal_with_errno(EXIT_FAILURE, "clock_gettime failed"); } } } else { t->tv_sec = time(NULL); t->tv_nsec = 0; } }
static int try_connect(void) { int pipefd, ret; struct sockaddr_un saddr; memset(&saddr, '\0', sizeof(saddr)); saddr.sun_family = AF_UNIX; snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", pipefn); pipefd = socket(AF_UNIX, SOCK_STREAM, 0); if (pipefd < 0) fatal_with_errno(EXIT_FAILURE, "socket"); ret = connect(pipefd, (const struct sockaddr *) &saddr, sizeof(saddr)); if (ret != -1) return pipefd; return -1; }
int main(int argc, char **argv) { int interval = 30, i; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; struct passwd *new_uid = NULL; const char *pidfilebase = prog; logformat = DEFAULT_LOGFORMAT; user = RUN_AS_USER; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:")) != -1) { switch(i) { case 'h': help(prog); break; case 's': monhost = optarg; break; case 'l': logfn = optarg; break; case 'i': interval = atoi(optarg); break; case 'f': logformat = optarg; break; case 'u': user = optarg; break; case 'V': exit(EXIT_SUCCESS); case 'p': pidfilebase = optarg; break; } } argc -= optind; argv += optind; /* not enough args for the old way? */ if ((argc == 1) || (argc == 2)) help(prog); /* see if it's being called in the old style - 3 or 4 args */ /* <system> <logfn> <interval> [<format>] */ if (argc >= 3) { monhost = argv[0]; logfn = argv[1]; interval = atoi(argv[2]); } if (argc >= 4) { /* read out the remaining argv entries to the format string */ logformat = xmalloc(LARGEBUF); memset(logformat, '\0', LARGEBUF); for (i = 3; i < argc; i++) snprintfcat(logformat, LARGEBUF, "%s ", argv[i]); } if (!monhost) fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s <system>"); if (!logfn) fatalx(EXIT_FAILURE, "No filename defined for logging - use -l <file>"); /* shouldn't happen */ if (!logformat) fatalx(EXIT_FAILURE, "No format defined - but this should be impossible"); printf("logging status of %s to %s (%is intervals)\n", monhost, logfn, interval); if (upscli_splitname(monhost, &upsname, &hostname, &port) != 0) { fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); } if (upscli_connect(&ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) fprintf(stderr, "Warning: initial connect failed: %s\n", upscli_strerror(&ups)); if (strcmp(logfn, "-") == 0) logfile = stdout; else logfile = fopen(logfn, "a"); if (logfile == NULL) fatal_with_errno(EXIT_FAILURE, "could not open logfile %s", logfn); /* now drop root if we have it */ new_uid = get_user_pwent(user); open_syslog(prog); if (logfile != stdout) background(); setup_signals(); writepid(pidfilebase); become_user(new_uid); compile_format(); while (exit_flag == 0) { time(&now); if (nextpoll > now) { /* there is still time left, so sleep it off */ sleep(difftime(nextpoll, now)); nextpoll += interval; } else { /* we spent more time in polling than the interval allows */ nextpoll = now + interval; } if (reopen_flag) { upslogx(LOG_INFO, "Signal %d: reopening log file", reopen_flag); reopen_log(); reopen_flag = 0; } /* reconnect if necessary */ if (upscli_fd(&ups) < 0) { upscli_connect(&ups, hostname, port, 0); } run_flist(); /* don't keep connection open if we don't intend to use it shortly */ if (interval > 30) { upscli_disconnect(&ups); } } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); if (logfile != stdout) fclose(logfile); upscli_disconnect(&ups); exit(EXIT_SUCCESS); }
void upsdrv_initups(void) { #ifndef TESTING const struct { const char *val; const int dtr; const int rts; } cablepower[] = { { "normal", 1, 0 }, /* default */ { "reverse", 0, 1 }, { "both", 1, 1 }, { "none", 0, 0 }, { NULL } }; int i; const char *val; struct termios tio; /* * Open and lock the serial port and set the speed to 2400 baud. */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* * Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = '\r'; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } val = getval("cablepower"); for (i = 0; val && cablepower[i].val; i++) { if (!strcasecmp(val, cablepower[i].val)) { break; } } if (!cablepower[i].val) { fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val); } ser_set_dtr(upsfd, cablepower[i].dtr); ser_set_rts(upsfd, cablepower[i].rts); /* * Allow some time to settle for the cablepower */ usleep(100000); #endif blazer_initups(); }
/* On success, fill in the curDevice structure and return the report * descriptor length. On failure, return -1. * Note: When callback is not NULL, the report descriptor will be * passed to this function together with the udev and USBDevice_t * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. If it isn't accepted, the next device * (if any) will be tried, until there are no more devices left. */ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, unsigned char *rdbuf, int rdlen)) { #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; #endif int rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; struct usb_device *dev; struct usb_bus *bus; usb_dev_handle *udev; struct usb_interface_descriptor *iface; int ret, res; unsigned char buf[20]; unsigned char *p; char string[256]; int i; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; int rdlen; /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ libusb_close(*udevp); #endif for (bus = usb_busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); /* supported vendors are now checked by the supplied matcher */ /* open the device */ *udevp = udev = usb_open(dev); if (!udev) { upsdebugx(2, "Failed to open device, skipping. (%s)", usb_strerror()); continue; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(curDevice->Vendor); free(curDevice->Product); free(curDevice->Serial); free(curDevice->Bus); memset(curDevice, '\0', sizeof(*curDevice)); curDevice->VendorID = dev->descriptor.idVendor; curDevice->ProductID = dev->descriptor.idProduct; curDevice->Bus = strdup(bus->dirname); if (dev->descriptor.iManufacturer) { ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, string, sizeof(string)); if (ret > 0) { curDevice->Vendor = strdup(string); } } if (dev->descriptor.iProduct) { ret = usb_get_string_simple(udev, dev->descriptor.iProduct, string, sizeof(string)); if (ret > 0) { curDevice->Product = strdup(string); } } if (dev->descriptor.iSerialNumber) { ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string)); if (ret > 0) { curDevice->Serial = strdup(string); } } upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); upsdebugx(2, "Trying to match device"); for (m = matcher; m; m=m->next) { ret = matches(m, curDevice); if (ret==0) { upsdebugx(2, "Device does not match - skipping"); goto next_device; } else if (ret==-1) { fatal_with_errno(EXIT_FAILURE, "matcher"); goto next_device; } else if (ret==-2) { upsdebugx(2, "matcher: unspecified error"); goto next_device; } } upsdebugx(2, "Device matches"); /* Now we have matched the device we wanted. Claim it. */ #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ retries = 3; while (usb_claim_interface(udev, 0) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); if (usb_detach_kernel_driver_np(udev, 0) < 0) { upsdebugx(2, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(2, "detached kernel driver from USB device..."); } if (retries-- > 0) { continue; } fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #else if (usb_claim_interface(udev, 0) < 0) { fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #endif /* set default interface */ usb_set_altinterface(udev, 0); if (!callback) { return 1; } if (!dev->config) { /* ?? this should never happen */ upsdebugx(2, " Couldn't retrieve descriptors"); goto next_device; } rdlen1 = -1; rdlen2 = -1; /* Get HID descriptor */ /* FIRST METHOD: ask for HID descriptor directly. */ /* res = usb_get_descriptor(udev, USB_DT_HID, 0, buf, 0x9); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_HID << 8) + 0, 0, buf, 0x9, USB_TIMEOUT); if (res < 0) { upsdebugx(2, "Unable to get HID descriptor (%s)", usb_strerror()); } else if (res < 9) { upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); } else { upsdebug_hex(3, "HID descriptor, method 1", buf, 9); rdlen1 = buf[7] | (buf[8] << 8); } if (rdlen1 < -1) { upsdebugx(2, "Warning: HID descriptor, method 1 failed"); } /* SECOND METHOD: find HID descriptor among "extra" bytes of interface descriptor, i.e., bytes tucked onto the end of descriptor 2. */ /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), only this second method gives the correct result */ /* for now, we always assume configuration 0, interface 0, altsetting 0, as above. */ iface = &dev->config[0].interface[0].altsetting[0]; for (i=0; i<iface->extralen; i+=iface->extra[i]) { upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, iface->extra[i], iface->extra[i+1]); if (i+9 <= iface->extralen && iface->extra[i] >= 9 && iface->extra[i+1] == 0x21) { p = &iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); rdlen2 = p[7] | (p[8] << 8); break; } } if (rdlen2 < -1) { upsdebugx(2, "Warning: HID descriptor, method 2 failed"); } /* when available, always choose the second value, as it seems to be more reliable (it is the one reported e.g. by lsusb). Note: if the need arises, can change this to use the maximum of the two values instead. */ rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; if (rdlen < 0) { upsdebugx(2, "Unable to retrieve any HID descriptor"); goto next_device; } if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { upsdebugx(2, "Warning: two different HID descriptors retrieved (Reportlen = %d vs. %d)", rdlen1, rdlen2); } upsdebugx(2, "HID descriptor length %d", rdlen); if (rdlen > (int)sizeof(rdbuf)) { upsdebugx(2, "HID descriptor too long %d (max %d)", rdlen, (int)sizeof(rdbuf)); goto next_device; } /* res = usb_get_descriptor(udev, USB_DT_REPORT, 0, bigbuf, rdlen); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_REPORT << 8) + 0, 0, rdbuf, rdlen, USB_TIMEOUT); if (res < 0) { upsdebug_with_errno(2, "Unable to get Report descriptor"); goto next_device; } if (res < rdlen) { upsdebugx(2, "Warning: report descriptor too short (expected %d, got %d)", rdlen, res); rdlen = res; /* correct rdlen if necessary */ } res = callback(udev, curDevice, rdbuf, rdlen); if (res < 1) { upsdebugx(2, "Caller doesn't like this device"); goto next_device; } upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); upsdebugx(2, "Found HID device"); fflush(stdout); return rdlen; next_device: usb_close(udev); } } *udevp = NULL; upsdebugx(2, "No appropriate HID device found"); fflush(stdout); return -1; }
static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *handle, USBDevice_t *device)) { struct usb_bus *bus; /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ usb_device_close(*handlep); #endif for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; usb_dev_handle *handle; for (dev = bus->devices; dev; dev = dev->next) { int i, ret; USBDeviceMatcher_t *m; upsdebugx(4, "Checking USB device [%04x:%04x] (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); /* supported vendors are now checked by the supplied matcher */ /* open the device */ *handlep = handle = usb_open(dev); if (!handle) { upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror()); continue; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(device->Vendor); free(device->Product); free(device->Serial); free(device->Bus); memset(device, 0, sizeof(*device)); device->VendorID = dev->descriptor.idVendor; device->ProductID = dev->descriptor.idProduct; device->Bus = strdup(bus->dirname); if (dev->descriptor.iManufacturer) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iManufacturer, buf, sizeof(buf)); if (ret > 0) { device->Vendor = strdup(buf); } } if (dev->descriptor.iProduct) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iProduct, buf, sizeof(buf)); if (ret > 0) { device->Product = strdup(buf); } } if (dev->descriptor.iSerialNumber) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iSerialNumber, buf, sizeof(buf)); if (ret > 0) { device->Serial = strdup(buf); } } upsdebugx(4, "- VendorID : %04x", device->VendorID); upsdebugx(4, "- ProductID : %04x", device->ProductID); upsdebugx(4, "- Manufacturer : %s", device->Vendor ? device->Vendor : "unknown"); upsdebugx(4, "- Product : %s", device->Product ? device->Product : "unknown"); upsdebugx(4, "- Serial Number: %s", device->Serial ? device->Serial : "unknown"); upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown"); for (m = matcher; m; m = m->next) { switch (m->match_function(device, m->privdata)) { case 0: upsdebugx(4, "Device does not match - skipping"); goto next_device; case -1: fatal_with_errno(EXIT_FAILURE, "matcher"); goto next_device; case -2: upsdebugx(4, "matcher: unspecified error"); goto next_device; } } for (i = 0; i < 3; i++) { ret = callback(handle, device); if (ret >= 0) { upsdebugx(4, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID); return ret; } #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ if (usb_detach_kernel_driver_np(handle, 0) < 0) { upsdebugx(4, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(4, "detached kernel driver from USB device..."); } #endif } fatalx(EXIT_FAILURE, "USB device [%04x:%04x] matches, but driver callback failed: %s", device->VendorID, device->ProductID, usb_strerror()); next_device: usb_close(handle); } } *handlep = NULL; upsdebugx(4, "No matching USB device found"); return -1; }
static void start_daemon(int lockfd) { int maxfd, pid, pipefd, ret; struct timeval tv; fd_set rfds; conn_t *tmp, *tmpnext; us_serialize(SERIALIZE_INIT); if ((pid = fork()) < 0) fatal_with_errno(EXIT_FAILURE, "Unable to enter background"); if (pid != 0) { /* parent */ /* wait for child to set up the listener */ us_serialize(SERIALIZE_WAIT); return; } /* child */ close(0); close(1); close(2); /* make fds 0-2 point somewhere defined */ if (open("/dev/null", O_RDWR) != 0) fatal_with_errno(EXIT_FAILURE, "open /dev/null"); if (dup(0) == -1) fatal_with_errno(EXIT_FAILURE, "dup"); if (dup(0) == -1) fatal_with_errno(EXIT_FAILURE, "dup"); pipefd = open_sock(); if (verbose) upslogx(LOG_INFO, "Timer daemon started"); /* release the parent */ us_serialize(SERIALIZE_SET); /* drop the lock now that the background is running */ unlink(lockfn); close(lockfd); /* now watch for activity */ for (;;) { /* wait at most 1s so we can check our timers regularly */ tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(pipefd, &rfds); maxfd = pipefd; for (tmp = connhead; tmp != NULL; tmp = tmp->next) { FD_SET(tmp->fd, &rfds); if (tmp->fd > maxfd) maxfd = tmp->fd; } ret = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (ret > 0) { if (FD_ISSET(pipefd, &rfds)) conn_add(pipefd); tmp = connhead; while (tmp) { tmpnext = tmp->next; if (FD_ISSET(tmp->fd, &rfds)) { if (sock_read(tmp) < 0) { close(tmp->fd); conn_del(tmp); } } tmp = tmpnext; } } checktimers(); } }
/* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ int blazer_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING int ret; if (udev == NULL) { ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); if (ret < 1) { return ret; } } ret = (*subdriver_command)(cmd, buf, buflen); if (ret >= 0) { return ret; } switch (ret) { case -EBUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ fatal_with_errno(EXIT_FAILURE, "Permissions problem"); case -EPIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; } #ifdef ETIME case -ETIME: /* Timer expired */ #endif if (usb_reset(udev) == 0) { upsdebugx(1, "Device reset handled"); } case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ usb->close(udev); udev = NULL; break; case -ETIMEDOUT: /* Connection timed out */ case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif default: break; } return ret; #else const struct { const char *command; const char *answer; } testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 2.27 30.0 00101000\r" }, { "F\r", "#230.0 000 024.0 50.0\r" }, { "I\r", "#------------- ------ VT12046Q \r" }, { NULL } }; int i; memset(buf, 0, buflen); for (i = 0; testing[i].command; i++) { if (strcasecmp(cmd, testing[i].command)) { continue; } return snprintf(buf, buflen, "%s", testing[i].answer); } return snprintf(buf, buflen, "%s", testing[i].command); #endif }
void upsdrv_initups(void) { struct termios tio; int baud = B1200; char *str; if ((str = getval("baudrate")) != NULL) { int temp = atoi(str); switch (temp) { case 300: baud = B300; break; case 600: baud = B600; break; case 1200: baud = B1200; break; case 2400: baud = B2400; break; case 4800: baud = B4800; break; case 9600: baud = B9600; break; case 19200: baud = B19200; break; case 38400: baud = B38400; break; default: fatalx(EXIT_FAILURE, "Unrecognized baudrate: %s", str); } upsdebugx(1, "baud_rate = %d", temp); } upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, baud); if (tcgetattr(upsfd, &tio) != 0) fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path); tio.c_lflag = ICANON; tio.c_iflag |= IGNCR; /* Ignore CR */ tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 0; tcsetattr(upsfd, TCSANOW, &tio); if ((str = getval("input_timeout")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad input_timeout parameter: %s", str); input_timeout_sec = temp; } upsdebugx(1, "input_timeout = %d Sec", input_timeout_sec); if ((str = getval("output_pace")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad output_pace parameter: %s", str); output_pace_usec = temp; } upsdebugx(1, "output_pace = %d uSec", output_pace_usec); if ((str = getval("full_update_timer")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad full_update_timer parameter: %s", str); full_update_timer = temp; } upsdebugx(1, "full_update_timer = %d Sec", full_update_timer); use_crlf = testvar("use_crlf"); upsdebugx(1, "use_crlf = %d", use_crlf); use_pre_lf = testvar("use_pre_lf"); upsdebugx(1, "use_pre_lf = %d", use_pre_lf); }
void upsdrv_initups(void) { #ifndef TESTING const struct { const char *name; int (*command)(const char *cmd, char *buf, size_t buflen); } subdriver[] = { { "cypress", &cypress_command }, { "phoenix", &phoenix_command }, { "ippon", &ippon_command }, { "krauler", &krauler_command }, { NULL } }; int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ char *regex_array[6]; char *subdrv = getval("subdriver"); regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); /* check for language ID workaround (#1) */ if (getval("langid_fix")) { /* skip "0x" prefix and set back to hexadecimal */ if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) { upslogx(LOG_NOTICE, "Error enabling language ID workaround"); } else { upsdebugx(2, "language ID workaround enabled (using '0x%x')", langid_fix); } } /* pick up the subdriver name if set explicitly */ if (subdrv) { int i; if (!regex_array[0] || !regex_array[1]) { fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory."); } for (i = 0; subdriver[i].name; i++) { if (strcasecmp(subdrv, subdriver[i].name)) { continue; } subdriver_command = subdriver[i].command; break; } if (!subdriver_command) { fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", subdrv); } } ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch (ret) { case -1: fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); case 0: break; /* all is well */ default: fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); } /* link the matchers */ regex_matcher->next = &device_matcher; ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" "and make sure you have an up-to-date version of NUT. If this does not help,\n" "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" "options specified. Please refer to the man page for details about these options\n" "(man 8 blazer_usb).\n"); } if (!subdriver_command) { fatalx(EXIT_FAILURE, "No subdriver selected"); } /* create a new matcher for later reopening */ ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); if (ret) { fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); } /* link the matchers */ reopen_matcher->next = regex_matcher; dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); /* check for language ID workaround (#2) */ if (langid_fix != -1) { /* Future improvement: * Asking for the zero'th index is special - it returns a string * descriptor that contains all the language IDs supported by the * device. Typically there aren't many - often only one. The * language IDs are 16 bit numbers, and they start at the third byte * in the descriptor. See USB 2.0 specification, section 9.6.7, for * more information on this. * This should allow automatic application of the workaround */ ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf)); if (ret >= 4) { langid = tbuf[2] | (tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid); } } #endif blazer_initups(); }