int red_extension_read_eeprom_from_fs(uint8_t *buffer, int extension) { FILE *f; char file_name[128]; int length; if (robust_snprintf(file_name, sizeof(file_name), "/tmp/new_eeprom_extension_%d.conf", extension) < 0) { return -1; } if ((f = fopen(file_name, "rb")) == NULL) { return -1; } else { length = fread(buffer, 1, EEPROM_SIZE, f); if (fclose(f) < 0) { log_warn("Could not close file %s", file_name); } if (remove(file_name) < 0) { log_warn("Could not delete file %s", file_name); } return length; } }
// sets errno on error int conf_file_write(ConfFile *conf_file, const char *filename) { bool success = false; char filename_tmp[1024]; FILE *fp = NULL; int i; ConfFileLine *line; if (robust_snprintf(filename_tmp, sizeof(filename_tmp), "%s.tmp", filename) < 0) { goto cleanup; } // open <filename>.tmp for writing fp = fopen(filename_tmp, "wb"); // FIXME: don't create as 0666 in all cases if (fp == NULL) { goto cleanup; } // write lines to <filename>.tmp for (i = 0; i < conf_file->lines.count; ++i) { line = array_get(&conf_file->lines, i); // if raw is != NULL then this line does not contain a name/value pair if (line->raw != NULL) { if (robust_fwrite(fp, line->raw, strlen(line->raw)) < 0) { goto cleanup; } } else { if (conf_file_write_escaped(fp, line->name, true) < 0) { goto cleanup; } if (robust_fwrite(fp, " =", 2) < 0) { goto cleanup; } if (*line->value != '\0') { if (robust_fwrite(fp, " ", 1) < 0) { goto cleanup; } if (conf_file_write_escaped(fp, line->value, false) < 0) { goto cleanup; } } } if (robust_fwrite(fp, END_OF_LINE, strlen(END_OF_LINE)) < 0) { goto cleanup; } } robust_fclose(fp); fp = NULL; // rename <filename>.tmp to <filename>. use MoveFileEx on Windows instead // of rename, because rename cannot replace existing files on Windows #ifdef _WIN32 if (!MoveFileExA(filename_tmp, filename, MOVEFILE_REPLACE_EXISTING)) { errno = ERRNO_WINAPI_OFFSET + GetLastError(); goto cleanup; } #else if (rename(filename_tmp, filename) < 0) { goto cleanup; } #endif success = true; cleanup: robust_fclose(fp); return success ? 0 : -1; }
APIE program_scheduler_create(ProgramScheduler *program_scheduler, ProgramSchedulerProcessSpawnedFunction process_spawned, ProgramSchedulerStateChangedFunction state_changed, void *opaque) { int phase = 0; Program *program = containerof(program_scheduler, Program, scheduler); APIE error_code; char bin_directory[1024]; char *log_directory; String *dev_null_file_name; int i; String *environment; // format bin directory name if (robust_snprintf(bin_directory, sizeof(bin_directory), "%s/bin", program->root_directory->buffer) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not format program bin directory name: %s (%d)", get_errno_name(errno), errno); goto cleanup; } // create bin directory as default user (UID 1000, GID 1000) error_code = directory_create(bin_directory, DIRECTORY_FLAG_RECURSIVE, 0755, 1000, 1000); if (error_code != API_E_SUCCESS) { goto cleanup; } // format log directory name if (asprintf(&log_directory, "%s/log", program->root_directory->buffer) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not format program log directory name: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 1; // create log directory as default user (UID 1000, GID 1000) error_code = directory_create(log_directory, DIRECTORY_FLAG_RECURSIVE, 0755, 1000, 1000); if (error_code != API_E_SUCCESS) { goto cleanup; } // get '/dev/null' stock string object error_code = inventory_get_stock_string("/dev/null", &dev_null_file_name); if (error_code != API_E_SUCCESS) { goto cleanup; } phase = 2; program_scheduler->process_spawned = process_spawned; program_scheduler->state_changed = state_changed; program_scheduler->opaque = opaque; program_scheduler->absolute_working_directory = NULL; program_scheduler->absolute_stdin_file_name = NULL; program_scheduler->absolute_stdout_file_name = NULL; program_scheduler->absolute_stderr_file_name = NULL; program_scheduler->log_directory = log_directory; program_scheduler->dev_null_file_name = dev_null_file_name; program_scheduler->observer.function = program_scheduler_handle_observer; program_scheduler->observer.opaque = program_scheduler; program_scheduler->observer_state = PROCESS_OBSERVER_STATE_FINISHED; program_scheduler->shutdown = false; program_scheduler->waiting_for_brickd = !network_is_brickd_connected(); program_scheduler->timer_active = false; program_scheduler->cron_active = false; program_scheduler->last_spawned_process = NULL; program_scheduler->last_spawned_timestamp = 0; program_scheduler->state = PROGRAM_SCHEDULER_STATE_STOPPED; program_scheduler->timestamp = time(NULL); program_scheduler->message = NULL; // if X11 is enabled... if (_x11_enabled) { for (i = 0; i < program->config.environment->items.count; ++i) { environment = *(String **)array_get(&program->config.environment->items, i); // ...and the DISPLAY environment variable is set... if (strncmp(environment->buffer, "DISPLAY=", strlen("DISPLAY=")) == 0) { // ...then use the process monitor to wait for lxpanel to start program_scheduler->observer_state = PROCESS_OBSERVER_STATE_PENDING; break; } } } // FIXME: only create timer for interval mode, otherwise this wastes a // file descriptor per non-interval scheduler if (timer_create_(&program_scheduler->timer, program_scheduler_handle_timer, program_scheduler) < 0) { error_code = api_get_error_code_from_errno(); log_error("Could not create timer: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 3; cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 2: string_unlock_and_release(dev_null_file_name); case 1: free(log_directory); default: break; } return phase == 3 ? API_E_SUCCESS : error_code; }
static File *program_scheduler_prepare_continuous_log(ProgramScheduler *program_scheduler, struct timeval timestamp, const char *suffix) { struct tm localized_timestamp; char iso8601dt[64] = "unknown"; char iso8601usec[16] = ""; char iso8601tz[16] = ""; char buffer[1024]; APIE error_code; String *name; File *file; // format ISO 8601 date, time and timezone offset if (localtime_r(×tamp.tv_sec, &localized_timestamp) != NULL) { // can use common ISO 8601 format YYYY-MM-DDThh:mm:ss.uuuuuu±hhmm // because this timestamp is not part of a filename strftime(iso8601dt, sizeof(iso8601dt), "%Y-%m-%dT%H:%M:%S", &localized_timestamp); snprintf(iso8601usec, sizeof(iso8601usec), ".%06d", (int)timestamp.tv_usec); strftime(iso8601tz, sizeof(iso8601tz), "%z", &localized_timestamp); } // format log filename if (robust_snprintf(buffer, sizeof(buffer), "%s/continuous_%s.log", program_scheduler->log_directory, suffix) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format %s log file name: %s (%d)", suffix, get_errno_name(errno), errno); return NULL; } error_code = string_wrap(buffer, NULL, OBJECT_CREATE_FLAG_INTERNAL | OBJECT_CREATE_FLAG_LOCKED, NULL, &name); if (error_code != API_E_SUCCESS) { program_scheduler_handle_error(program_scheduler, true, "Could not wrap %s log file name into string object: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } // open log file error_code = file_open(name->base.id, FILE_FLAG_WRITE_ONLY | FILE_FLAG_CREATE | FILE_FLAG_APPEND, 0644, 1000, 1000, NULL, OBJECT_CREATE_FLAG_INTERNAL, NULL, &file); string_unlock_and_release(name); if (error_code != API_E_SUCCESS) { program_scheduler_handle_error(program_scheduler, true, "Could not open/create %s log file: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } // format header if (robust_snprintf(buffer, sizeof(buffer), "\n\n%s%s%s\n-------------------------------------------------------------------------------\n", iso8601dt, iso8601usec, iso8601tz) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format timestamp for %s log file: %s (%d)", suffix, get_errno_name(errno), errno); file_release(file); return NULL; } // write header if (write(file->fd, buffer, strlen(buffer)) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not write timestamp to %s log file: %s (%d)", suffix, get_errno_name(errno), errno); file_release(file); return NULL; } return file; }
static File *program_scheduler_prepare_individual_log(ProgramScheduler *program_scheduler, struct timeval timestamp, const char *suffix) { uint64_t microseconds = (uint64_t)timestamp.tv_sec * 1000000 + (uint64_t)timestamp.tv_usec; struct tm localized_timestamp; char iso8601[128] = "unknown"; char buffer[1024]; struct stat st; APIE error_code; int counter = 0; String *name; File *file; // format ISO 8601 date, time and timezone offset if (localtime_r(×tamp.tv_sec, &localized_timestamp) != NULL) { // use ISO 8601 format YYYYMMDDThhmmss±hhmm instead of the common // YYYY-MM-DDThh:mm:ss±hhmm because the colons in there can create // problems on Windows which does not allow colons in filenames strftime(iso8601, sizeof(iso8601), "%Y%m%dT%H%M%S%z", &localized_timestamp); } // create log file, include microsecond timestamp to reduce the chance for // file name collisions and as easy-to-parse timestamp for brickv if (robust_snprintf(buffer, sizeof(buffer), "%s/%s_%"PRIu64"_%s.log", program_scheduler->log_directory, iso8601, microseconds, suffix) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format %s log file name: %s (%d)", suffix, get_errno_name(errno), errno); return NULL; } while (counter < 1000) { // only try to create the log file if it's not already existing if (lstat(buffer, &st) < 0) { error_code = string_wrap(buffer, NULL, OBJECT_CREATE_FLAG_INTERNAL | OBJECT_CREATE_FLAG_LOCKED, NULL, &name); if (error_code != API_E_SUCCESS) { program_scheduler_handle_error(program_scheduler, true, "Could not wrap %s log file name into string object: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } error_code = file_open(name->base.id, FILE_FLAG_WRITE_ONLY | FILE_FLAG_CREATE | FILE_FLAG_EXCLUSIVE, 0644, 1000, 1000, NULL, OBJECT_CREATE_FLAG_INTERNAL, NULL, &file); string_unlock_and_release(name); if (error_code == API_E_SUCCESS) { return file; } // if file_open failed with an error different from API_E_ALREADY_EXISTS // then give up, there is no point trying to recover this situation if (error_code != API_E_ALREADY_EXISTS) { program_scheduler_handle_error(program_scheduler, true, "Could not create %s log file: %s (%d)", suffix, api_get_error_code_name(error_code), error_code); return NULL; } } if (robust_snprintf(buffer, sizeof(buffer), "%s/%s_%"PRIu64"+%03d_%s.log", program_scheduler->log_directory, iso8601, microseconds, ++counter, suffix) < 0) { program_scheduler_handle_error(program_scheduler, true, "Could not format %s log file name: %s (%d)", suffix, get_errno_name(errno), errno); return NULL; } } program_scheduler_handle_error(program_scheduler, true, "Could not create %s log file within 1000 attempts", suffix); return NULL; }
static int prepare_paths(void) { char *home; struct passwd *pw; char brickd_dirname[1024]; struct stat st; if (getuid() == 0) { return 0; } home = getenv("HOME"); if (home == NULL || *home == '\0') { pw = getpwuid(getuid()); if (pw == NULL) { fprintf(stderr, "Could not determine home directory: %s (%d)\n", get_errno_name(errno), errno); return -1; } home = pw->pw_dir; } if (robust_snprintf(brickd_dirname, sizeof(brickd_dirname), "%s/.brickd", home) < 0) { fprintf(stderr, "Could not format ~/.brickd directory name: %s (%d)\n", get_errno_name(errno), errno); return -1; } if (robust_snprintf(_config_filename, sizeof(_config_filename), "%s/.brickd/brickd.conf", home) < 0) { fprintf(stderr, "Could not format ~/.brickd/brickd.conf file name: %s (%d)\n", get_errno_name(errno), errno); return -1; } if (robust_snprintf(_pid_filename, sizeof(_pid_filename), "%s/.brickd/brickd.pid", home) < 0) { fprintf(stderr, "Could not format ~/.brickd/brickd.pid file name: %s (%d)\n", get_errno_name(errno), errno); return -1; } if (robust_snprintf(_log_filename, sizeof(_log_filename), "%s/.brickd/brickd.log", home) < 0) { fprintf(stderr, "Could not format ~/.brickd/brickd.log file name: %s (%d)\n", get_errno_name(errno), errno); return -1; } if (mkdir(brickd_dirname, 0755) < 0) { if (errno != EEXIST) { fprintf(stderr, "Could not create directory '%s': %s (%d)\n", brickd_dirname, get_errno_name(errno), errno); return -1; } if (stat(brickd_dirname, &st) < 0) { fprintf(stderr, "Could not get information for '%s': %s (%d)\n", brickd_dirname, get_errno_name(errno), errno); return -1; } if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "Expecting '%s' to be a directory\n", brickd_dirname); return -1; } } return 0; }
int inventory_load_programs(void) { bool success = false; DIR *dp; struct dirent *dirent; const char *identifier; char directory[1024]; char filename[1024]; APIE error_code; log_debug("Loading program configurations from '%s'", _programs_directory); dp = opendir(_programs_directory); if (dp == NULL) { if (errno == ENOENT) { // no programs directory, nothing to load return 0; } log_error("Could not open programs directory '%s': %s (%d)", _programs_directory, get_errno_name(errno), errno); return -1; } for (;;) { errno = 0; dirent = readdir(dp); if (dirent == NULL) { if (errno == 0) { // end-of-directory reached break; } else { log_error("Could not get next entry of programs directory '%s': %s (%d)", _programs_directory, get_errno_name(errno), errno); goto cleanup; } } if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0 || dirent->d_type != DT_DIR) { continue; } identifier = dirent->d_name; if (robust_snprintf(directory, sizeof(directory), "%s/%s", _programs_directory, identifier) < 0) { log_error("Could not format program directory name: %s (%d)", get_errno_name(errno), errno); goto cleanup; } if (robust_snprintf(filename, sizeof(filename), "%s/program.conf", directory) < 0) { log_error("Could not format program config file name: %s (%d)", get_errno_name(errno), errno); goto cleanup; } log_debug("Loading program from '%s'", directory); error_code = program_load(identifier, directory, filename); if (error_code != API_E_SUCCESS) { // load errors are non-fatal log_debug("Could not load program from '%s', ignoring program: %s (%d)", directory, api_get_error_code_name(error_code), error_code); } } success = true; cleanup: closedir(dp); return success ? 0 : -1; }
int inventory_init(void) { int phase = 0; struct passwd *pw; int type; log_debug("Initializing inventory subsystem"); // get home directory of the default user (UID 1000) pw = getpwuid(1000); if (pw == NULL) { log_error("Could not determine home directory for UID 1000: %s (%d)", get_errno_name(errno), errno); goto cleanup; } if (robust_snprintf(_programs_directory, sizeof(_programs_directory), "%s/programs", pw->pw_dir) < 0) { log_error("Could not format programs directory name: %s (%d)", get_errno_name(errno), errno); goto cleanup; } // create session array if (array_create(&_sessions, 32, sizeof(Session *), true) < 0) { log_error("Could not create session array: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 1; // create object arrays for (type = OBJECT_TYPE_STRING; type <= OBJECT_TYPE_PROGRAM; ++type) { if (array_create(&_objects[type], 32, sizeof(Object *), true) < 0) { log_error("Could not create %s object array: %s (%d)", object_get_type_name(type), get_errno_name(errno), errno); goto cleanup; } phase = 2; } // create stock string array if (array_create(&_stock_strings, 32, sizeof(String *), true) < 0) { log_error("Could not create stock string array: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 3; cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 2: for (--type; type >= OBJECT_TYPE_STRING; --type) { array_destroy(&_objects[type], inventory_destroy_object); } case 1: array_destroy(&_sessions, inventory_destroy_session); default: break; } return phase == 3 ? 0 : -1; }