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; }
/* * 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 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 struct script *script_create(struct timemaster_config *config) { struct script *script = xmalloc(sizeof(*script)); struct source *source, **sources; struct config_file *ntp_config = NULL; int **allocated_phcs = (int **)parray_new(); int ret = 0, shm_segment; script->configs = (struct config_file **)parray_new(); script->commands = (char ***)parray_new(); ntp_config = add_ntp_program(config, script); shm_segment = config->first_shm_segment; for (sources = config->sources; (source = *sources); sources++) { switch (source->type) { case NTP_SERVER: if (add_ntp_source(&source->ntp, &ntp_config->content)) ret = 1; break; case PTP_DOMAIN: if (add_ptp_source(&source->ptp, config, &shm_segment, &allocated_phcs, &ntp_config->content, script)) ret = 1; break; } } free_parray((void **)allocated_phcs); if (ret) { script_destroy(script); return NULL; } return script; }
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)); } }
/* copy contents of directory from_root into to_root */ void dir_copy_files(const char *from_root, const char *to_root) { int i; parray *files = parray_new(); /* don't copy root directory */ dir_list_file(files, from_root, NULL, true, false); for (i = 0; i < parray_num(files); i++) { pgFile *file = (pgFile *) parray_get(files, i); if (S_ISDIR(file->mode)) { char to_path[MAXPGPATH]; join_path_components(to_path, to_root, file->path + strlen(from_root) + 1); if (verbose && !check) printf(_("create directory \"%s\"\n"), file->path + strlen(from_root) + 1); if (!check) { dir_create_dir(to_path, DIR_PERMISSION); } continue; } else if(S_ISREG(file->mode)) { if (verbose && !check) printf(_("copy \"%s\"\n"), file->path + strlen(from_root) + 1); if (!check) copy_file(from_root, to_root, file, NO_COMPRESSION); } } /* cleanup */ parray_walk(files, pgFileFree); parray_free(files); }
/* * 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; }
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; }
/* * Delete backup files of the backup and update the status of the backup to * BACKUP_STATUS_DELETED. */ static int pgBackupDeleteFiles(pgBackup *backup) { int i; char path[MAXPGPATH]; char timestamp[20]; parray *files; /* * If the backup was deleted already, there is nothing to do. */ if (backup->status == BACKUP_STATUS_DELETED) return 0; time2iso(timestamp, lengthof(timestamp), backup->start_time); elog(INFO, "delete: %s", timestamp); /* * Update STATUS to BACKUP_STATUS_DELETING in preparation for the case which * the error occurs before deleting all backup files. */ if (!check) { backup->status = BACKUP_STATUS_DELETING; pgBackupWriteIni(backup); } /* list files to be deleted */ files = parray_new(); pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR); dir_list_file(files, path, NULL, true, true); /* delete leaf node first */ parray_qsort(files, pgFileComparePathDesc); for (i = 0; i < parray_num(files); i++) { pgFile *file = (pgFile *) parray_get(files, i); /* print progress */ elog(LOG, "delete file(%d/%lu) \"%s\"", i + 1, (unsigned long) parray_num(files), file->path); /* skip actual deletion in check mode */ if (!check) { if (remove(file->path)) { elog(WARNING, "can't remove \"%s\": %s", file->path, strerror(errno)); parray_walk(files, pgFileFree); parray_free(files); return 1; } } } /* * After deleting all of the backup files, update STATUS to * BACKUP_STATUS_DELETED. */ if (!check) { backup->status = BACKUP_STATUS_DELETED; pgBackupWriteIni(backup); } parray_walk(files, pgFileFree); parray_free(files); return 0; }