static struct config_file *add_ntp_program(struct timemaster_config *config, struct script *script) { struct config_file *ntp_config = xmalloc(sizeof(*ntp_config)); char **command = NULL; ntp_config->content = xstrdup(""); switch (config->ntp_program) { case CHRONYD: extend_config_string(&ntp_config->content, config->chronyd.settings); ntp_config->path = string_newf("%s/chrony.conf", config->rundir); command = get_chronyd_command(&config->chronyd, ntp_config); break; case NTPD: extend_config_string(&ntp_config->content, config->ntpd.settings); ntp_config->path = string_newf("%s/ntp.conf", config->rundir); command = get_ntpd_command(&config->ntpd, ntp_config); break; } parray_append((void ***)&script->configs, ntp_config); parray_append((void ***)&script->commands, command); return ntp_config; }
/* * List files, symbolic links and directories in the directory "root" and add * pgFile objects to "files". We add "root" to "files" if add_root is true. * * If the sub-directory name is in "exclude" list, the sub-directory itself is * listed but the contents of the sub-directory is ignored. * * When omit_symlink is true, symbolic link is ignored and only file or * directory llnked to will be listed. */ void dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root) { char path[MAXPGPATH]; char buf[MAXPGPATH * 2]; char black_item[MAXPGPATH * 2]; parray *black_list = NULL; join_path_components(path, backup_path, PG_BLACK_LIST); if (root && pgdata && strcmp(root, pgdata) == 0 && fileExists(path)) { FILE *black_list_file = NULL; black_list = parray_new(); black_list_file = fopen(path, "r"); if (black_list_file == NULL) ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not open black_list: %s", strerror(errno)))); while (fgets(buf, lengthof(buf), black_list_file) != NULL) { join_path_components(black_item, pgdata, buf); if (black_item[strlen(black_item) - 1] == '\n') black_item[strlen(black_item) - 1] = '\0'; if (black_item[0] == '#' || black_item[0] == '\0') continue; parray_append(black_list, black_item); } fclose(black_list_file); parray_qsort(black_list, BlackListCompare); dir_list_file_internal(files, root, exclude, omit_symlink, add_root, black_list); } else dir_list_file_internal(files, root, exclude, omit_symlink, add_root, NULL); }
static void extend_string_array(char ***a, char **strings) { char **s; for (s = strings; *s; s++) parray_append((void ***)a, xstrdup(*s)); }
static struct source *source_ptp_parse(char *parameter, char **settings) { char *name, *value; struct source *source; int r = 0; source = xmalloc(sizeof(*source)); source->type = PTP_DOMAIN; source->ptp.delay = DEFAULT_PTP_DELAY; source->ptp.ntp_poll = DEFAULT_PTP_NTP_POLL; source->ptp.phc2sys_poll = DEFAULT_PTP_PHC2SYS_POLL; source->ptp.interfaces = (char **)parray_new(); source->ptp.ptp4l_settings = (char **)parray_new(); source->ptp.ntp_options = xstrdup(""); if (parse_int(parameter, &source->ptp.domain)) { pr_err("invalid ptp_domain number %s", parameter); goto failed; } for (; *settings; settings++) { parse_setting(*settings, &name, &value); if (!strcasecmp(name, "delay")) { r = parse_double(value, &source->ptp.delay); } else if (!strcasecmp(name, "ntp_poll")) { r = parse_int(value, &source->ptp.ntp_poll); } else if (!strcasecmp(name, "phc2sys_poll")) { r = parse_int(value, &source->ptp.phc2sys_poll); } else if (!strcasecmp(name, "ptp4l_option")) { parray_append((void ***)&source->ptp.ptp4l_settings, xstrdup(value)); } else if (!strcasecmp(name, "ntp_options")) { replace_string(value, &source->ptp.ntp_options); } else if (!strcasecmp(name, "interfaces")) { parse_words(value, &source->ptp.interfaces); } else { pr_err("unknown ptp_domain setting %s", name); goto failed; } if (r) { pr_err("invalid value %s for %s", value, name); goto failed; } } if (!*source->ptp.interfaces) { pr_err("no interfaces specified for ptp_domain %d", source->ptp.domain); goto failed; } return source; failed: source_destroy(source); return NULL; }
static void init_program_config(struct program_config *config, const char *name, ...) { const char *s; va_list ap; config->path = xstrdup(name); config->settings = (char **)parray_new(); config->options = (char **)parray_new(); va_start(ap, name); /* add default options and settings */ while ((s = va_arg(ap, const char *))) parray_append((void ***)&config->options, xstrdup(s)); while ((s = va_arg(ap, const char *))) parray_append((void ***)&config->settings, xstrdup(s)); va_end(ap); }
static int parse_section(char **settings, char *name, struct timemaster_config *config) { struct source *source = NULL; char ***settings_dst = NULL; char *parameter = parse_word(name); if (!strcasecmp(name, "ntp_server")) { source = source_ntp_parse(parameter, settings); if (!source) return 1; } else if (!strcasecmp(name, "ptp_domain")) { source = source_ptp_parse(parameter, settings); if (!source) return 1; } else if (!strcasecmp(name, "chrony.conf")) { settings_dst = &config->chronyd.settings; } else if (!strcasecmp(name, "ntp.conf")) { settings_dst = &config->ntpd.settings; } else if (!strcasecmp(name, "ptp4l.conf")) { settings_dst = &config->ptp4l.settings; } else if (!strcasecmp(name, "chronyd")) { if (parse_program_settings(settings, &config->chronyd)) return 1; } else if (!strcasecmp(name, "ntpd")) { if (parse_program_settings(settings, &config->ntpd)) return 1; } else if (!strcasecmp(name, "phc2sys")) { if (parse_program_settings(settings, &config->phc2sys)) return 1; } else if (!strcasecmp(name, "ptp4l")) { if (parse_program_settings(settings, &config->ptp4l)) return 1; } else if (!strcasecmp(name, "timemaster")) { if (parse_timemaster_settings(settings, config)) return 1; } else { pr_err("unknown section %s", name); return 1; } if (source) parray_append((void ***)&config->sources, source); if (settings_dst) { free_parray((void **)*settings_dst); *settings_dst = (char **)parray_new(); extend_string_array(settings_dst, settings); } return 0; }
static void parse_words(char *s, char ***a) { char *w; if (**a) { free_parray((void **)(*a)); *a = (char **)parray_new(); } while (*s) { w = s; s = parse_word(s); parray_append((void ***)a, xstrdup(w)); } }
/* * Construct parray of pgFile from the file list. * If root is not NULL, path will be absolute path. */ parray * dir_read_file_list(const char *root, const char *file_txt) { FILE *fp; parray *files; char buf[MAXPGPATH * 2]; fp = fopen(file_txt, "rt"); if (fp == NULL) ereport(ERROR, ((errno == ENOENT ? errcode(ERROR_CORRUPTED) : errcode(ERROR_SYSTEM)), errmsg("could not open \"%s\": %s", file_txt, strerror(errno)))); files = parray_new(); while (fgets(buf, lengthof(buf), fp)) { char path[MAXPGPATH]; char type; unsigned long write_size; pg_crc32c crc; unsigned int mode; /* bit length of mode_t depends on platforms */ struct tm tm; pgFile *file; memset(&tm, 0, sizeof(tm)); if (sscanf(buf, "%s %c %lu %u %o %d-%d-%d %d:%d:%d", path, &type, &write_size, &crc, &mode, &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 11) { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid format found in \"%s\"", file_txt))); } if (type != 'f' && type != 'F' && type != 'd' && type != 'l') { ereport(ERROR, (errcode(ERROR_CORRUPTED), errmsg("invalid type '%c' found in \"%s\"", type, file_txt))); } tm.tm_isdst = -1; file = (pgFile *) pgut_malloc(offsetof(pgFile, path) + (root ? strlen(root) + 1 : 0) + strlen(path) + 1); tm.tm_year -= 1900; tm.tm_mon -= 1; file->mtime = mktime(&tm); file->mode = mode | ((type == 'f' || type == 'F') ? S_IFREG : type == 'd' ? S_IFDIR : type == 'l' ? S_IFLNK : 0); file->size = 0; file->read_size = 0; file->write_size = write_size; file->crc = crc; file->is_datafile = (type == 'F' ? true : false); file->linked = NULL; if (root) sprintf(file->path, "%s/%s", root, path); else strcpy(file->path, path); parray_append(files, file); } fclose(fp); /* file.txt is sorted, so this qsort is redundant */ parray_qsort(files, pgFileComparePath); return files; }
void dir_list_file_internal(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root, parray *black_list) { pgFile *file; file = pgFileNew(root, omit_symlink); if (file == NULL) return; /* skip if the file is in black_list defined by user */ if (black_list && parray_bsearch(black_list, root, BlackListCompare)) { /* found in black_list. skip this item */ return; } if (add_root) parray_append(files, file); /* chase symbolic link chain and find regular file or directory */ while (S_ISLNK(file->mode)) { ssize_t len; char linked[MAXPGPATH]; len = readlink(file->path, linked, sizeof(linked)); if (len == -1) { ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not read link \"%s\": %s", file->path, strerror(errno)))); } linked[len] = '\0'; file->linked = pgut_strdup(linked); /* make absolute path to read linked file */ if (linked[0] != '/') { char dname[MAXPGPATH]; char absolute[MAXPGPATH]; strncpy(dname, file->path, lengthof(dname)); join_path_components(absolute, dirname(dname), linked); file = pgFileNew(absolute, omit_symlink); } else file = pgFileNew(file->linked, omit_symlink); /* linked file is not found, stop following link chain */ if (file == NULL) return; parray_append(files, file); } /* * If the entry was a directory, add it to the list and add call this * function recursivelly. * If the directory name is in the exclude list, do not list the contents. */ while (S_ISDIR(file->mode)) { int i; bool skip = false; DIR *dir; struct dirent *dent; char *dirname; /* skip entry which matches exclude list */ dirname = strrchr(file->path, '/'); if (dirname == NULL) dirname = file->path; else dirname++; /* * If the item in the exclude list starts with '/', compare to the * absolute path of the directory. Otherwise compare to the directory * name portion. */ for (i = 0; exclude && exclude[i]; i++) { if (exclude[i][0] == '/') { if (strcmp(file->path, exclude[i]) == 0) { skip = true; break; } } else { if (strcmp(dirname, exclude[i]) == 0) { skip = true; break; } } } if (skip) break; /* open directory and list contents */ dir = opendir(file->path); if (dir == NULL) { if (errno == ENOENT) { /* maybe the direcotry was removed */ return; } ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not open directory \"%s\": %s", file->path, strerror(errno)))); } errno = 0; while ((dent = readdir(dir))) { char child[MAXPGPATH]; /* skip entries point current dir or parent dir */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; join_path_components(child, file->path, dent->d_name); dir_list_file_internal(files, child, exclude, omit_symlink, true, black_list); } if (errno && errno != ENOENT) { int errno_tmp = errno; closedir(dir); ereport(ERROR, (errcode(ERROR_SYSTEM), errmsg("could not read directory \"%s\": %s", file->path, strerror(errno_tmp)))); } closedir(dir); break; /* pseudo loop */ } parray_qsort(files, pgFileComparePath); }
static int add_ptp_source(struct ptp_domain *source, struct timemaster_config *config, int *shm_segment, int ***allocated_phcs, char **ntp_config, struct script *script) { struct config_file *config_file; char **command, *uds_path, **interfaces; int i, j, num_interfaces, *phc, *phcs, hw_ts; struct sk_ts_info ts_info; pr_debug("adding PTP domain %d", source->domain); hw_ts = SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; for (num_interfaces = 0; source->interfaces[num_interfaces]; num_interfaces++) ; if (!num_interfaces) return 0; /* get PHCs used by specified interfaces */ phcs = xmalloc(num_interfaces * sizeof(int)); for (i = 0; i < num_interfaces; i++) { phcs[i] = -1; /* check if the interface has a usable PHC */ if (sk_get_ts_info(source->interfaces[i], &ts_info)) { pr_err("failed to get time stamping info for %s", source->interfaces[i]); free(phcs); return 1; } if (!ts_info.valid || ((ts_info.so_timestamping & hw_ts) != hw_ts)) { pr_debug("interface %s: no PHC", source->interfaces[i]); continue; } pr_debug("interface %s: PHC %d", source->interfaces[i], ts_info.phc_index); /* and the PHC isn't already used in another source */ for (j = 0; (*allocated_phcs)[j]; j++) { if (*(*allocated_phcs)[j] == ts_info.phc_index) { pr_debug("PHC %d already allocated", ts_info.phc_index); break; } } if (!(*allocated_phcs)[j]) phcs[i] = ts_info.phc_index; } for (i = 0; i < num_interfaces; i++) { /* skip if already used by ptp4l in this domain */ if (phcs[i] == -2) continue; interfaces = (char **)parray_new(); parray_append((void ***)&interfaces, source->interfaces[i]); /* merge all interfaces sharing PHC to one ptp4l command */ if (phcs[i] >= 0) { for (j = i + 1; j < num_interfaces; j++) { if (phcs[i] == phcs[j]) { parray_append((void ***)&interfaces, source->interfaces[j]); /* mark the interface as used */ phcs[j] = -2; } } /* don't use this PHC in other sources */ phc = xmalloc(sizeof(int)); *phc = phcs[i]; parray_append((void ***)allocated_phcs, phc); } uds_path = string_newf("%s/ptp4l.%d.socket", config->rundir, *shm_segment); config_file = xmalloc(sizeof(*config_file)); config_file->path = string_newf("%s/ptp4l.%d.conf", config->rundir, *shm_segment); config_file->content = xstrdup("[global]\n"); extend_config_string(&config_file->content, config->ptp4l.settings); extend_config_string(&config_file->content, source->ptp4l_settings); string_appendf(&config_file->content, "slaveOnly 1\n" "domainNumber %d\n" "uds_address %s\n", source->domain, uds_path); if (phcs[i] >= 0) { /* HW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, interfaces, 1); parray_append((void ***)&script->commands, command); command = get_phc2sys_command(&config->phc2sys, source->domain, source->phc2sys_poll, *shm_segment, uds_path); parray_append((void ***)&script->commands, command); } else { /* SW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, interfaces, 0); parray_append((void ***)&script->commands, command); string_appendf(&config_file->content, "clock_servo ntpshm\n" "ntpshm_segment %d\n", *shm_segment); } parray_append((void ***)&script->configs, config_file); add_shm_source(*shm_segment, source->ntp_poll, source->phc2sys_poll, source->delay, source->ntp_options, "PTP", config, ntp_config); (*shm_segment)++; free(uds_path); free(interfaces); } free(phcs); return 0; }
static struct timemaster_config *config_parse(char *path) { struct timemaster_config *config = xcalloc(1, sizeof(*config)); FILE *f; char buf[4096], *line, *section_name = NULL; char **section_lines = NULL; int ret = 0; config->sources = (struct source **)parray_new(); config->ntp_program = DEFAULT_NTP_PROGRAM; config->rundir = xstrdup(DEFAULT_RUNDIR); config->first_shm_segment = DEFAULT_FIRST_SHM_SEGMENT; init_program_config(&config->chronyd, "chronyd", NULL, DEFAULT_CHRONYD_SETTINGS, NULL); init_program_config(&config->ntpd, "ntpd", NULL, DEFAULT_NTPD_SETTINGS, NULL); init_program_config(&config->phc2sys, "phc2sys", DEFAULT_PHC2SYS_OPTIONS, NULL, NULL); init_program_config(&config->ptp4l, "ptp4l", DEFAULT_PTP4L_OPTIONS, NULL, NULL); f = fopen(path, "r"); if (!f) { pr_err("failed to open %s: %m", path); free(config); return NULL; } while (fgets(buf, sizeof(buf), f)) { /* remove trailing and leading whitespace */ for (line = buf + strlen(buf) - 1; line >= buf && isspace(*line); line--) *line = '\0'; for (line = buf; *line && isspace(*line); line++) ; /* skip comments and empty lines */ if (!*line || *line == '#') continue; if (*line == '[') { /* parse previous section before starting another */ if (section_name) { if (parse_section(section_lines, section_name, config)) { ret = 1; break; } free_parray((void **)section_lines); free(section_name); } section_name = parse_section_name(line); section_lines = (char **)parray_new(); continue; } if (!section_lines) { pr_err("settings outside section"); ret = 1; break; } parray_append((void ***)§ion_lines, xstrdup(line)); } if (!ret && section_name && parse_section(section_lines, section_name, config)) { ret = 1; } fclose(f); if (section_name) free(section_name); if (section_lines) free_parray((void **)section_lines); if (ret) { config_destroy(config); return NULL; } return config; }