void event_process(void) { Event *event; AtCommand *at; SList *list, *prev_link = NULL, *next_link, *expired_link, tmp_link; int minute_tick, five_minute_tick, ten_minute_tick, fifteen_minute_tick, thirty_minute_tick, hour_tick, day_tick; struct tm *tm_now; static struct tm tm_prev; static time_t t_prev; time(&pikrellcam.t_now); pikrellcam.second_tick = (pikrellcam.t_now == t_prev) ? FALSE : TRUE; t_prev = pikrellcam.t_now; if (pikrellcam.second_tick) { tm_prev = pikrellcam.tm_local; tm_now = localtime(&pikrellcam.t_now); minute_tick = (tm_now->tm_min != tm_prev.tm_min) ? TRUE : FALSE; pikrellcam.tm_local = *tm_now; } else minute_tick = FALSE; if (pikrellcam.state_modified || minute_tick) { pikrellcam.state_modified = FALSE; state_file_write(); } tmp_link.next = NULL; for (list = event_list; list; prev_link = list, list = list->next) { /* Event loop processing is done with the list unlocked until we | have an expired link that must be removed from the list so that | called event functions cad add events to the list. | If another thread adds an event, only the last list->next | will be modified and this loop will catch it or it won't. */ event = (Event *) list->data; if ( (event->time == 0 && event->count == 0) || event->func == NULL ) /* not activated */ continue; if (event->time > 0 && event->time > pikrellcam.t_now) continue; else if (event->count > 0 && --event->count > 0) continue; if (pikrellcam.verbose) printf("Event func -> [%s] period=%d\n", event->name, (int) event->period); if (event->func) (*event->func)(event->data); if (event->period > 0) event->time += event->period; else { pthread_mutex_lock(&event_mutex); expired_link = list; if (list == event_list) { event_list = list->next; tmp_link.next = list->next; list = &tmp_link; } else { next_link = list->next; list = prev_link; list->next = next_link; } free(expired_link->data); free(expired_link); pthread_mutex_unlock(&event_mutex); } } if (minute_tick) { char *p, buf[IBUF_LEN]; int i, n, minute_now, minute_at, minute_offset = 0; static int start = TRUE; static int at_notify_fd, at_notify_wd; struct inotify_event *event; if (at_notify_fd == 0) { at_notify_fd = inotify_init(); if (at_notify_fd > 0) { fcntl(at_notify_fd, F_SETFL, fcntl(at_notify_fd, F_GETFL) | O_NONBLOCK); at_notify_wd = inotify_add_watch(at_notify_fd, pikrellcam.config_dir, IN_CREATE | IN_MODIFY); } } else if (at_notify_wd > 0) { n = read(at_notify_fd, buf, IBUF_LEN); if (n > 0) { for (i = 0; i < n; i += sizeof(*event) + event->len) { event = (struct inotify_event *) &buf[i]; if ( event->len > 0 && !strcmp(event->name, PIKRELLCAM_AT_COMMANDS_CONFIG) ) at_commands_config_load(pikrellcam.at_commands_config_file); } } } tm_now = &pikrellcam.tm_local; minute_now = tm_now->tm_hour * 60 + tm_now->tm_min; five_minute_tick = ((tm_now->tm_min % 5) == 0) ? TRUE : FALSE; ten_minute_tick = ((tm_now->tm_min % 10) == 0) ? TRUE : FALSE; fifteen_minute_tick = ((tm_now->tm_min % 15) == 0) ? TRUE : FALSE; thirty_minute_tick = ((tm_now->tm_min % 10) == 0) ? TRUE : FALSE; hour_tick = (tm_now->tm_hour != tm_prev.tm_hour) ? TRUE : FALSE; day_tick = (tm_now->tm_mday != tm_prev.tm_mday) ? TRUE : FALSE; if (day_tick || !sun.initialized) { if (sun.initialized) { char tbuf[32]; log_printf_no_timestamp("\n========================================================\n"); strftime(tbuf, sizeof(tbuf), "%F", localtime(&pikrellcam.t_now)); log_printf_no_timestamp("%s ================== New Day ==================\n", tbuf); log_printf_no_timestamp("========================================================\n"); strftime(tbuf, sizeof(tbuf), "%F", localtime(&pikrellcam.t_now)); } sun_times_init(); sun.initialized = TRUE; log_lines(); state_file_write(); } for (list = at_command_list; list; list = list->next) { at = (AtCommand *) list->data; if ((p = strchr(at->at_time, '+')) != NULL) minute_offset = atoi(p + 1); else if ((p = strchr(at->at_time, '-')) != NULL) minute_offset = -atoi(p + 1); if (!strcmp(at->at_time, "start")) minute_at = start ? minute_now : 0; else if (!strcmp(at->at_time, "minute")) minute_at = minute_now; else if (!strcmp(at->at_time, "5minute")) minute_at = five_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "10minute")) minute_at = ten_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "15minute")) minute_at = fifteen_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "30minute")) minute_at = thirty_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "hour")) minute_at = hour_tick ? minute_now : 0; else if (!strncmp(at->at_time, "dawn", 4)) minute_at = sun.dawn + minute_offset; else if (!strncmp(at->at_time, "dusk", 4)) minute_at = sun.dusk + minute_offset; else if (!strncmp(at->at_time, "sunrise", 7)) minute_at = sun.sunrise + minute_offset; else if (!strncmp(at->at_time, "sunset", 6)) minute_at = sun.sunset + minute_offset; else if (!strncmp(at->at_time, "nautical_dawn", 13)) minute_at = sun.nautical_dawn + minute_offset; else if (!strncmp(at->at_time, "nautical_dusk", 13)) minute_at = sun.nautical_dusk + minute_offset; else { minute_at = (int) strtol(at->at_time, &p, 10) * 60; if (*p == ':') minute_at += strtol(p + 1, NULL, 10); else { minute_at = 0; /* error in at_time string */ log_printf("Error in at_command: [%s] bad at_time: [%s]\n", at->command, at->at_time); } } if (minute_now != minute_at) continue; /* Have a time match so check frequency. */ if ( !strcmp(at->frequency, "daily") || ( !strcmp(at->frequency, "Sat-Sun") && (tm_now->tm_wday == 0 || tm_now->tm_wday == 6) ) || ( !strcmp(at->frequency, "Mon-Fri") && tm_now->tm_wday > 0 && tm_now->tm_wday < 6 ) || ( (p = strstr(weekdays, at->frequency)) != NULL && (p - weekdays) / 3 == tm_now->tm_wday ) ) { if (*(at->command) == '@') command_process(at->command + 1); else exec_no_wait(at->command, NULL); } } start = FALSE; if (pikrellcam.config_modified) config_save(pikrellcam.config_file); } }
int main(int argc, char *argv[]) { int fifo; int i, n; char *opt, *arg, *equal_arg, *homedir, *user; char *line, *eol, buf[4096]; pgm_name = argv[0]; bcm_host_init(); time(&pikrellcam.t_now); config_set_defaults(); for (i = 1; i < argc; i++) get_arg_pass1(argv[i]); if (!config_load(pikrellcam.config_file)) config_save(pikrellcam.config_file); if (!motion_regions_config_load(pikrellcam.motion_regions_config_file)) motion_regions_config_save(pikrellcam.motion_regions_config_file); if (!at_commands_config_load(pikrellcam.at_commands_config_file)) at_commands_config_save(pikrellcam.at_commands_config_file); for (i = 1; i < argc; i++) { if (get_arg_pass1(argv[i])) continue; opt = argv[i]; /* Just for initial install-pikrellcam.sh run to create config files. */ if (!strcmp(opt, "-quit")) exit(0); /* Accept: --opt arg -opt arg opt=arg --opt=arg -opt=arg */ for (i = 0; i < 2; ++i) if (*opt == '-') ++opt; if ((equal_arg = strchr(opt, '=')) != NULL) { *equal_arg++ = '\0'; arg = equal_arg; ++i; } else arg = argv[i + 1]; /* For camera parameters, do not set the camera, only replace | values in the parameter table. */ if ( !config_set_option(opt, arg, TRUE) && !mmalcam_config_parameter_set(opt, arg, FALSE) ) { log_printf("Bad arg: %s\n", opt); exit(1); } } homedir = getpwuid(geteuid())->pw_dir; user = strrchr(homedir, '/'); pikrellcam.effective_user = strdup(user ? user + 1 : "pi"); if (*pikrellcam.log_file != '/') { snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.log_file); dup_string(&pikrellcam.log_file, buf); } if (*pikrellcam.media_dir != '/') { snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, pikrellcam.media_dir); dup_string(&pikrellcam.media_dir, buf); } strftime(buf, sizeof(buf), "%F %T", localtime(&pikrellcam.t_now)); log_printf("\n%s ==== PiKrellCam started ====\n", buf); snprintf(buf, sizeof(buf), "%s/%s", pikrellcam.install_dir, "www"); check_modes(buf, 0775); asprintf(&pikrellcam.command_fifo, "%s/www/FIFO", pikrellcam.install_dir); asprintf(&pikrellcam.script_dir, "%s/scripts", pikrellcam.install_dir); asprintf(&pikrellcam.mjpeg_filename, "%s/mjpeg.jpg", pikrellcam.mjpeg_dir); log_printf("using FIFO: %s\n", pikrellcam.command_fifo); log_printf("using mjpeg: %s\n", pikrellcam.mjpeg_filename); /* Subdirs must match www/config.php and the init script is supposed | to take care of that. */ asprintf(&pikrellcam.video_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_VIDEO_SUBDIR); asprintf(&pikrellcam.still_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_STILL_SUBDIR); asprintf(&pikrellcam.timelapse_dir, "%s/%s", pikrellcam.media_dir, PIKRELLCAM_TIMELAPSE_SUBDIR); if (!make_dir(pikrellcam.media_dir)) exit(1); snprintf(buf, sizeof(buf), "%s/scripts-dist/init $I $m $M $P $G", pikrellcam.install_dir); exec_wait(buf, NULL); /* User may have enabled a mount disk on media_dir */ exec_wait(pikrellcam.on_startup_cmd, NULL); check_modes(pikrellcam.media_dir, 0775); if ( !make_dir(pikrellcam.mjpeg_dir) || !make_dir(pikrellcam.video_dir) || !make_dir(pikrellcam.still_dir) || !make_dir(pikrellcam.timelapse_dir) || !make_fifo(pikrellcam.command_fifo) ) exit(1); if ((fifo = open(pikrellcam.command_fifo, O_RDONLY | O_NONBLOCK)) < 0) { log_printf("Failed to open FIFO: %s. %m\n", pikrellcam.command_fifo); exit(1); } fcntl(fifo, F_SETFL, 0); read(fifo, buf, sizeof(buf)); sun_times_init(); camera_start(); config_timelapse_load_status(); signal(SIGINT, signal_quit); signal(SIGTERM, signal_quit); signal(SIGCHLD, event_child_signal); while (1) { usleep(1000000 / EVENT_LOOP_FREQUENCY); event_process(); /* Process lines in the FIFO. Single lines via an echo "xxx" > FIFO | or from a web page may not have a terminating \n. | Local scripts may dump multiple \n terminated lines into the FIFO. */ if ((n = read(fifo, buf, sizeof(buf) - 2)) > 0) { if (buf[n - 1] != '\n') buf[n++] = '\n'; /* ensures all lines in buf end in \n */ buf[n] = '\0'; line = buf; eol = strchr(line, '\n'); while (eol > line) { *eol++ = '\0'; command_process(line); while (*eol == '\n') ++eol; line = eol; eol = strchr(line, '\n'); } } } return 0; }