/* 1-Wire Emergency Button Daemon v2. * * Sensiplicity Systems * * Copyright 2015 * * Department of Botany and Plant Pathology * Center for Genome Research and Biocomputing * Oregon State University * Corvallis, OR 97331 * * This program is not free software; you can not redistribute it and/or * modify it at all. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Usage: * emergency-button-daemon <1-wire ID> [ <1-wire ID> ...] * If the switch is activated, or the given id is not present, * EmailProgram (see below) will be called with the given id, * and the light will be turned on. The switch's state will * be polled every second. * * If an error occurs during initialization, errors will be logged * to stderr and the program will exit with a status of -1. * During normal operation, changes in state will be logged to stderr. */ #include #include #include #include #include #include #include #include #include #include const char *Usage = "emergency-button-daemon <1-wire ID> [ <1-wire ID> ...]\n" "If the switch is activated, or the given id disappears,\n" "%s will be called with the given id,\n" "and the light will be turned on.\n\n" "The switch's state will be polled every second.\n"; /* The email program will be called when the button state changes. * It should accept the sensor ID as the first command line argument, * and ON, OFF, DISCONNECTED, or ERROR as its second argument. */ const char *EmailProgram = "/opt/tempberrypi/bin/email_emergency.py"; const char *SysBasePath = "/sys/devices/w1_bus_master1/"; /* Returns 1 if the switch on the device at the given state path is activated, 0 otherwise. On error, 2 is returned. */ int switch_activated(const char *statepath); /* Set the state of the light on the device at the given output path. If state == 0, the light is off; otherwise it is on. */ void set_light(int state, const char *outputpath); /* Print the usage message. */ void print_usage(void); /* Construct state and output paths from the given device id string * and base path. basepath should end with a slash. Returns 0 on success, * -1 on error. */ int construct_paths(const char *basepath, const char *id, char **statepath, char **outpath); enum buttonstate_t {START, ACTIVE, MISSING, NOT_ACTIVE}; /* Call the given program with an arbitrary first command-line argument * and a second command-line argument giving the button state. */ void call_program(const char *prog, const char *arg, enum buttonstate_t state); int main(int argc, char *argv[]){ if(argc < 3 || 0 == (argc % 2)){ print_usage(); return -1; } struct button_info_t { char *id; char *statepath; char *outpath; int email_interval; time_t prev_transmit_time; enum buttonstate_t prevstate; int statechange; int firstrun; } *button; button = malloc(sizeof(struct button_info_t) * ((argc - 1) / 2)); if(NULL == button){ fprintf(stderr, "emergency-button-daemon: error: malloc failure\n"); return -1; } int i,a; for(i=0, a=1; a < argc-1; i++,a+=2){ button[i].statechange = 0; button[i].firstrun = 1; button[i].prevstate = START; button[i].id = argv[a+1]; button[i].prev_transmit_time = 0; button[i].email_interval = strtol(argv[a], NULL, 10); if(errno == EINVAL || errno == ERANGE || button[i].email_interval <= 0){ fprintf(stderr, "emergency-button-daemon: error: invalid email interval %s\n", argv[a]); print_usage(); return -1; } if(construct_paths(SysBasePath, argv[a+1], &(button[i].statepath), &(button[i].outpath))){ fprintf(stderr, "emergency-button-daemon: error: malloc failure\n"); return -1; } int testfd = open(button[i].statepath, O_RDONLY); if(-1 == testfd){ fprintf(stderr, "emergency-button-daemon: error: could not open button path %s\n", button[i].statepath); perror("emergency-button-daemon"); print_usage(); return -1; } close(testfd); } while(1){ for(int i=0; i < ((argc-1)/2); i++){ int r; r = switch_activated(button[i].statepath); if(0 == r){ if(button[i].prevstate != NOT_ACTIVE){ printf("Switch %s NOT Activated\n", button[i].id); button[i].prevstate = NOT_ACTIVE; button[i].statechange = 1; button[i].prev_transmit_time = 0; } set_light(0, button[i].outpath); } else { if(1 == r){ if(button[i].prevstate != ACTIVE){ printf("Switch %s Activated\n", button[i].id); button[i].prevstate = ACTIVE; button[i].statechange = 1; } } else { if(button[i].prevstate != MISSING){ printf("Switch %s Missing!\n", button[i].id); button[i].prevstate = MISSING; button[i].statechange = 1; } } set_light(1, button[i].outpath); } if(button[i].firstrun){ button[i].statechange = 0; button[i].firstrun = 0; } if(ACTIVE == button[i].prevstate || MISSING == button[i].prevstate){ if(time(NULL) > button[i].prev_transmit_time + (60 * button[i].email_interval)){ call_program(EmailProgram, button[i].id, button[i].prevstate); button[i].prev_transmit_time = time(NULL); } } else if(button[i].statechange){ call_program(EmailProgram, button[i].id, button[i].prevstate); if(NOT_ACTIVE != button[i].prevstate)button[i].prev_transmit_time = time(NULL); button[i].statechange = 0; } } waitpid(-1, NULL, WNOHANG); /* Reap any zombie processes. */ sleep(1); } return 0; } /* Returns 1 if the switch on the device at the given state path is activated, 0 otherwise. On error, 2 is returned. */ int switch_activated(const char *statepath){ char buf; int count; int statefd; for(int tries=0; tries < 3; tries++){ statefd = open(statepath, O_RDONLY); if(statefd != -1)break; } if(-1 == statefd){ perror("emergency-button-daemon"); return 2; } for(int tries=0;tries < 3; tries++){ count = read(statefd, &buf, 1); if(1 == count)break; } close(statefd); if(count != 1){ return 2; } else { if(buf & 1){ return 0; } else { return 1; } } } /* Set the state of the light on the device at the given output path. If state == 0, the light is off; otherwise it is on. */ void set_light(int state, const char *outputpath){ char buf; int outputfd; outputfd = open(outputpath, O_WRONLY); if(-1 == outputfd){ perror("emergency-button-daemon"); return; } buf = (state) ? 1 : 3; write(outputfd, &buf, 1); close(outputfd); } /* Print the usage message. */ void print_usage(void){ fprintf(stderr, Usage, EmailProgram); } /* Construct state and output paths from the given device id string * and base path. basepath should end with a slash. Returns 0 on success, * -1 on error. */ int construct_paths(const char *basepath, const char *id, char **statepath, char **outpath){ if(NULL == basepath || NULL == id || NULL == statepath || NULL == outpath){ return -1; } const char *statestr = "state"; const char *outstr = "output"; *statepath = malloc(strlen(basepath) + strlen(id) + 1 + strlen(statestr) + 1); if(NULL == *statepath){ return -1; } *outpath = malloc(strlen(basepath) + strlen(id) + 1 + strlen(outstr) + 1); if(NULL == *outpath){ free(*statepath); return -1; } memmove(*statepath, basepath, strlen(basepath)); memmove(*statepath + strlen(basepath), id, strlen(id)); *(*statepath + strlen(basepath) + strlen(id)) = '/'; memmove(*outpath, *statepath, strlen(basepath) + strlen(id) + 1); memmove(*statepath + strlen(basepath) + strlen(id) + 1, statestr, strlen(statestr)); *(*statepath + strlen(basepath) + strlen(id) + strlen(statestr) + 1) = '\0'; memmove(*outpath + strlen(basepath) + strlen(id) + 1, outstr, strlen(outstr)); *(*outpath + strlen(basepath) + strlen(id) + strlen(outstr) + 1) = '\0'; return 0; } /* Call the given program with an arbitrary first command-line argument * and a second command-line argument giving the button state. */ void call_program(const char *prog, const char *arg, enum buttonstate_t state){ char *statestr; switch(state){ case ACTIVE: statestr = "ON"; break; case MISSING: statestr = "DISCONNECTED"; break; case NOT_ACTIVE: statestr = "OFF"; break; default: statestr = "ERROR"; break; } int r = fork(); if(-1 == r){ fprintf(stderr, "emergency-button-daemon: error: couldn't fork\n"); perror("emergency-button-daemon"); } if(0 == r){ if(execl(prog, prog, arg, statestr, (char *) NULL)){ fprintf(stderr, "emergency-button-daemon: error: couldn't execute %s\n", prog); perror("emergency-button-daemon"); } exit(0); } }