config_t *cfg_open(const char *path) { FILE *file = NULL; config_t *config = (config_t*)malloc(sizeof(config_t)); if (config == NULL) { return NULL; } if (path == NULL || path[0] == 0) { config_init(config); return config; } file = fopen(path,"r"); if (file == NULL) { config_init(config); return config; } // Not initializing produces a crash config_init(config); if (config_read(config,file) != CONFIG_TRUE) { #ifdef DEBUG if (config_error_type(config) == CONFIG_ERR_FILE_IO) { printf("Error opening configuration file.\n"); } else { printf("Error parsing configuration file.\n"); printf("Line: %d\n", config_error_line(config)); printf("Text: %s\n", config_error_text(config)); } #endif // Destroy the config and initialize a new one config_destroy(config); config_init(config); } fclose(file); config_set_tab_width(config,0); return config; }
/** * Re-reads configuration from the given file */ int read_config() { config_t cfg; config_init(&cfg); if (config_read_file(&cfg, conf.config_file) == CONFIG_FALSE) { if (config_error_type(&cfg) == CONFIG_ERR_FILE_IO) { log_error("Configuration file not found: %s", conf.config_file); config_destroy(&cfg); return -1; } log_error("%s:%d - %s", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); return -1; } int intVal; const char* charVal; if (config_lookup_int(&cfg, "num_connections", &intVal) == CONFIG_TRUE) { conf.num_connections = intVal; } if (config_lookup_int(&cfg, "report_freq", &intVal) == CONFIG_TRUE) { conf.report_freq = intVal; } if (config_lookup_int(&cfg, "packet_size", &intVal) == CONFIG_TRUE) { if (intVal > MAXPACKET) { log_error("packet_size: %d exceeds maximal packet size (%d)", intVal, MAXPACKET); config_destroy(&cfg); return -1; } conf.packet_size = intVal; } if (config_lookup_int(&cfg, "packets_count", &intVal) == CONFIG_TRUE) { conf.packets_count = intVal; } if (config_lookup_string(&cfg, "reports_dir", &charVal) == CONFIG_TRUE) { free(conf.reports_dir); conf.reports_dir = strdup(charVal); } if (config_lookup_string(&cfg, "hosts_file", &charVal) == CONFIG_TRUE) { free(conf.hosts_file); conf.hosts_file = strdup(charVal); } if (config_lookup_string(&cfg, "log_file", &charVal) == CONFIG_TRUE) { free(conf.log_file); conf.log_file = strdup(charVal); } config_destroy(&cfg); if (read_hosts() == -1) { return -1; } return 0; }
int parse_options(int argc, char **argv) { // there are potential memory leaks here -- it's called a second time, previously allocated strings will dangle. char *raw_service_name = NULL; /* Used to pick up the service name before possibly expanding it */ char *stuffing = NULL; /* used for picking up the stuffing option */ signed char c; /* used for argument parsing */ int i = 0; /* used for tracking options */ poptContext optCon; /* context for parsing command-line options */ struct poptOption optionsTable[] = { {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', NULL}, {"disconnectFromOutput", 'D', POPT_ARG_NONE, NULL, 0, NULL}, {"reconnectToOutput", 'R', POPT_ARG_NONE, NULL, 0, NULL}, {"kill", 'k', POPT_ARG_NONE, NULL, 0, NULL}, {"daemon", 'd', POPT_ARG_NONE, &config.daemonise, 0, NULL}, {"configfile", 'c', POPT_ARG_STRING, &config.configfile, 0, NULL}, {"statistics", 0, POPT_ARG_NONE, &config.statistics_requested, 0, NULL}, {"version", 'V', POPT_ARG_NONE, NULL, 0, NULL}, {"port", 'p', POPT_ARG_INT, &config.port, 0, NULL}, {"name", 'a', POPT_ARG_STRING, &raw_service_name, 0, NULL}, {"output", 'o', POPT_ARG_STRING, &config.output_name, 0, NULL}, {"on-start", 'B', POPT_ARG_STRING, &config.cmd_start, 0, NULL}, {"on-stop", 'E', POPT_ARG_STRING, &config.cmd_stop, 0, NULL}, {"wait-cmd", 'w', POPT_ARG_NONE, &config.cmd_blocking, 0, NULL}, {"mdns", 'm', POPT_ARG_STRING, &config.mdns_name, 0, NULL}, {"latency", 'L', POPT_ARG_INT, &config.userSuppliedLatency, 0, NULL}, {"AirPlayLatency", 'A', POPT_ARG_INT, &config.AirPlayLatency, 0, NULL}, {"iTunesLatency", 'i', POPT_ARG_INT, &config.iTunesLatency, 0, NULL}, {"forkedDaapdLatency", 0, POPT_ARG_INT, &config.ForkedDaapdLatency, 0, NULL}, {"stuffing", 'S', POPT_ARG_STRING, &stuffing, 'S', NULL}, {"resync", 'r', POPT_ARG_INT, &config.resyncthreshold, 0, NULL}, {"timeout", 't', POPT_ARG_INT, &config.timeout, 't', NULL}, {"password", 0, POPT_ARG_STRING, &config.password, 0, NULL}, {"tolerance", 0, POPT_ARG_INT, &config.tolerance, 0, NULL}, #ifdef CONFIG_METADATA {"metadata-pipename", 'M', POPT_ARG_STRING, &config.metadata_pipename, 'M', NULL}, {"get-coverart", 'g', POPT_ARG_NONE, &config.get_coverart, 'g', NULL}, #endif POPT_AUTOHELP {NULL, 0, 0, NULL, 0}}; // we have to parse the command line arguments to look for a config file int optind; optind = argc; int j; for (j = 0; j < argc; j++) if (strcmp(argv[j], "--") == 0) optind = j; optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0); poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); /* Now do options processing just to get a debug level */ debuglev = 0; while ((c = poptGetNextOpt(optCon)) >= 0) { switch (c) { case 'v': debuglev++; break; } } if (c < -1) { die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c)); } config_setting_t *setting; const char *str=0; int value=0; debug(1,"Looking for the configuration file \"%s\".",config.configfile); config_init(&config_file_stuff); char *config_file_real_path = realpath(config.configfile, NULL); if (config_file_real_path==NULL) { debug(2,"Can't resolve the configuration file \"%s\".",config.configfile); } else { debug(2, "Looking for configuration file at full path \"%s\"", config_file_real_path); /* Read the file. If there is an error, report it and exit. */ if (config_read_file(&config_file_stuff, config_file_real_path)) { // make config.cfg point to it config.cfg = &config_file_stuff; /* Get the Service Name. */ if (config_lookup_string(config.cfg, "general.name", &str)) { raw_service_name = (char *)str; } /* Get the Daemonize setting. */ if (config_lookup_string(config.cfg, "general.daemonize", &str)) { if (strcasecmp(str, "no") == 0) config.daemonise = 0; else if (strcasecmp(str, "yes") == 0) config.daemonise = 1; else die("Invalid daemonize option choice \"%s\". It should be \"yes\" or \"no\""); } /* Get the mdns_backend setting. */ if (config_lookup_string(config.cfg, "general.mdns_backend", &str)) config.mdns_name = (char *)str; /* Get the output_backend setting. */ if (config_lookup_string(config.cfg, "general.output_backend", &str)) config.output_name = (char *)str; /* Get the port setting. */ if (config_lookup_int(config.cfg, "general.port", &value)) { if ((value < 0) || (value > 65535)) die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 5000", value); else config.port = value; } /* Get the udp port base setting. */ if (config_lookup_int(config.cfg, "general.udp_port_base", &value)) { if ((value < 0) || (value > 65535)) die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 6001", value); else config.udp_port_base = value; } /* Get the udp port range setting. This is number of ports that will be tried for free ports , starting at the port base. Only three ports are needed. */ if (config_lookup_int(config.cfg, "general.udp_port_range", &value)) { if ((value < 0) || (value > 65535)) die("Invalid port range \"%sd\". It should be between 0 and 65535, default is 100", value); else config.udp_port_range = value; } /* Get the password setting. */ if (config_lookup_string(config.cfg, "general.password", &str)) config.password = (char *)str; if (config_lookup_string(config.cfg, "general.interpolation", &str)) { if (strcasecmp(str, "basic") == 0) config.packet_stuffing = ST_basic; else if (strcasecmp(str, "soxr") == 0) config.packet_stuffing = ST_soxr; else die("Invalid interpolation option choice \"%s\". It should be \"basic\" or \"soxr\""); } /* Get the statistics setting. */ if (config_lookup_string(config.cfg, "general.statistics", &str)) { if (strcasecmp(str, "no") == 0) config.statistics_requested = 0; else if (strcasecmp(str, "yes") == 0) config.statistics_requested = 1; else die("Invalid statistics option choice \"%s\". It should be \"yes\" or \"no\""); } /* Get the drift tolerance setting. */ if (config_lookup_int(config.cfg, "general.drift", &value)) config.tolerance = value; /* Get the resync setting. */ if (config_lookup_int(config.cfg, "general.resync_threshold", &value)) config.resyncthreshold = value; /* Get the verbosity setting. */ if (config_lookup_int(config.cfg, "general.log_verbosity", &value)) { if ((value >= 0) && (value <= 3)) debuglev = value; else die("Invalid log verbosity setting option choice \"%d\". It should be between 0 and 3, " "inclusive.", value); } /* Get the ignore_volume_control setting. */ if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) { if (strcasecmp(str, "no") == 0) config.ignore_volume_control = 0; else if (strcasecmp(str, "yes") == 0) config.ignore_volume_control = 1; else die("Invalid ignore_volume_control option choice \"%s\". It should be \"yes\" or \"no\""); } /* Get the playback_mode setting */ if (config_lookup_string(config.cfg, "general.playback_mode", &str)) { if (strcasecmp(str, "stereo") == 0) config.playback_mode = ST_stereo; else if (strcasecmp(str, "mono") == 0) config.playback_mode = ST_mono; else die("Invalid playback_mode choice \"%s\". It should be \"stereo\" (default) or \"mono\""); } /* Get the regtype -- the service type and protocol, separated by a dot. Default is "_raop._tcp" */ if (config_lookup_string(config.cfg, "general.regtype", &str)) config.regtype = strdup(str); /* Get the volume range, in dB, that should be used If not set, it means you just use the range set by the mixer. */ if (config_lookup_int(config.cfg, "general.volume_range_db", &value)) { if ((value < 30) || (value > 150)) die("Invalid volume range \"%sd\". It should be between 30 and 150 dB. Zero means use the mixer's native range", value); else config.volume_range_db = value; } /* Get the default latency. Deprecated! */ if (config_lookup_int(config.cfg, "latencies.default", &value)) config.userSuppliedLatency = value; /* Get the itunes latency. Deprecated! */ if (config_lookup_int(config.cfg, "latencies.itunes", &value)) config.iTunesLatency = value; /* Get the AirPlay latency. Deprecated! */ if (config_lookup_int(config.cfg, "latencies.airplay", &value)) config.AirPlayLatency = value; /* Get the forkedDaapd latency. Deprecated! */ if (config_lookup_int(config.cfg, "latencies.forkedDaapd", &value)) config.ForkedDaapdLatency = value; #ifdef CONFIG_METADATA /* Get the metadata setting. */ if (config_lookup_string(config.cfg, "metadata.enabled", &str)) { if (strcasecmp(str, "no") == 0) config.metadata_enabled = 0; else if (strcasecmp(str, "yes") == 0) config.metadata_enabled = 1; else die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\""); } if (config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) { if (strcasecmp(str, "no") == 0) config.get_coverart = 0; else if (strcasecmp(str, "yes") == 0) config.get_coverart = 1; else die("Invalid metadata include_cover_art option choice \"%s\". It should be \"yes\" or " "\"no\""); } if (config_lookup_string(config.cfg, "metadata.pipe_name", &str)) { config.metadata_pipename = (char *)str; } if (config_lookup_string(config.cfg, "metadata.socket_address", &str)) { config.metadata_sockaddr = (char *)str; } if (config_lookup_int(config.cfg, "metadata.socket_port", &value)) { config.metadata_sockport = value; } config.metadata_sockmsglength = 500; if (config_lookup_int(config.cfg, "metadata.socket_msglength", &value)) { config.metadata_sockmsglength = value < 500 ? 500 : value > 65000 ? 65000 : value; } if (config_lookup_int(config.cfg, "metadata.pipe_timeout", &value)) { if ((value < 1) || (value > 150000)) die("Invalid timeout range \"%sd\". It should be in the range 1 to 150,000 milliseconds.", value); else config.metadata_pipe_timeout = value; } #endif if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_play_begins", &str)) { config.cmd_start = (char *)str; } if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_play_ends", &str)) { config.cmd_stop = (char *)str; } if (config_lookup_string(config.cfg, "sessioncontrol.wait_for_completion", &str)) { if (strcasecmp(str, "no") == 0) config.cmd_blocking = 0; else if (strcasecmp(str, "yes") == 0) config.cmd_blocking = 1; else die("Invalid session control wait_for_completion option choice \"%s\". It should be " "\"yes\" or \"no\""); } if (config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) { config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0 if (strcasecmp(str, "no") == 0) config.allow_session_interruption = 0; else if (strcasecmp(str, "yes") == 0) config.allow_session_interruption = 1; else die("Invalid session control allow_interruption option choice \"%s\". It should be \"yes\" " "or \"no\""); } if (config_lookup_int(config.cfg, "sessioncontrol.session_timeout", &value)) { config.timeout = value; config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0 } } else { if (config_error_type(&config_file_stuff) == CONFIG_ERR_FILE_IO) debug(1, "Error reading configuration file \"%s\": \"%s\".", config_error_file(&config_file_stuff), config_error_text(&config_file_stuff)); else { die("Line %d of the configuration file \"%s\":\n%s", config_error_line(&config_file_stuff), config_error_file(&config_file_stuff), config_error_text(&config_file_stuff)); } } free(config_file_real_path); } // now, do the command line options again, but this time do them fully -- it's a unix convention that command line // arguments have precedence over configuration file settings. optind = argc; for (j = 0; j < argc; j++) if (strcmp(argv[j], "--") == 0) optind = j; optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0); poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); /* Now do options processing, get portname */ int tdebuglev = 0; while ((c = poptGetNextOpt(optCon)) >= 0) { switch (c) { case 'v': tdebuglev++; break; case 't': if (config.timeout == 0) { config.dont_check_timeout = 1; config.allow_session_interruption = 1; } else { config.dont_check_timeout = 0; config.allow_session_interruption = 0; } break; #ifdef CONFIG_METADATA case 'M': config.metadata_enabled = 1; break; case 'g': if (config.metadata_enabled == 0) die("If you want to get cover art, you must also select the --metadata-pipename option."); break; #endif case 'S': if (strcmp(stuffing, "basic") == 0) config.packet_stuffing = ST_basic; else if (strcmp(stuffing, "soxr") == 0) #ifdef HAVE_LIBSOXR config.packet_stuffing = ST_soxr; #else die("soxr option not available -- this version of shairport-sync was built without libsoxr " "support"); #endif else die("Illegal stuffing option \"%s\" -- must be \"basic\" or \"soxr\"", stuffing); break; } }
odesys_t * odesys_parse_cfg_from_buffer_ctor (const char *buffer) /* Parse the buffer and return an odesys_t object fully initialized and ready to use. */ { config_t cfg; odesys_t *odesys = NULL; molecule_t *molecule = NULL; laser_collection_t *lasers = NULL; /* Parse configurtion file into a libconfig config_t object. */ config_init (&cfg); if (config_read_string (&cfg, buffer) == CONFIG_FALSE) { switch (config_error_type (&cfg)) { case CONFIG_ERR_PARSE: fprintf (stderr, "Error in configuration at line %d\n", config_error_line (&cfg)); fprintf (stderr, "Error was: \"%s\"\n", config_error_text (&cfg)); return NULL; default: fprintf (stderr, "Unknown error in parsing configuration.\n"); return NULL; } } /* Parse the config_t object. */ molecule = molecule_cfg_parse_ctor (&cfg); if (molecule == NULL) { fprintf (stderr, "Failed to parse molecule information from configuration file.\n"); config_destroy (&cfg); return NULL; } lasers = laser_collection_cfg_parse_ctor (&cfg); if (lasers == NULL) { fprintf (stderr, "Failed to parse laser information from configuration file.\n"); molecule->dtor (molecule); config_destroy (&cfg); return NULL; } odesys = odesys_ctor (molecule, lasers); if (odesys == NULL) { fprintf (stderr, "Failed to initialize odesys.\n"); molecule->dtor (molecule); laser_collection_dtor (lasers); config_destroy (&cfg); return NULL; } if (odesys_cfg_parse (odesys, &cfg) < 0) { fprintf (stderr, "%s %d: Failed to parse odesys information.\n", __func__, __LINE__); odesys_dtor (odesys); config_destroy (&cfg); return NULL; } config_destroy (&cfg); return odesys; }
// TODO: error handling // TODO: better exit point handling // (freeing resources for valgrind-cleanliness is too tedious at the moment) void options_parse(int argc, char * argv[], struct Options * options) { int opt; // modified when config is loaded on the command line config_t config; config_init(&config); set_default_config(&config); // only to used for -g: print default config config_t config_default; config_init(&config_default); set_default_config(&config_default); // -i flag should only print options after all other // command line arguments have been applied bool print_options = false; while (-1 != (opt = getopt(argc, argv, OPTSTR))) { switch (opt) { case 'c': config_set_auto_convert(&config, CONFIG_TRUE); if (CONFIG_FALSE == config_read_file(&config, optarg)) { // failure reporting fprintf(stderr, "Failed to use config file '%s'!\n", optarg); switch (config_error_type(&config)) { case CONFIG_ERR_NONE: fprintf(stderr, "\tno error reported\n" "\t(This might be a libconfig problem.)\n"); break; case CONFIG_ERR_FILE_IO: fprintf(stderr, "\tfile I/O error\n"); break; case CONFIG_ERR_PARSE: fprintf(stderr, "\tparse error on line %d:\n" "\t%s\n", config_error_line(&config), config_error_text(&config)); break; default: fprintf(stderr, "\tunknown error\n" "\t(A new libconfig version might have introduced" " new kinds of warnings.)\n"); } } break; case 'g': config_write(&config_default, stdout); // be valgrind-clean config_destroy(&config_default); config_destroy(&config); exit(EXIT_SUCCESS); break; case 'i': print_options = true; break; default: /* '?' 'h' */ options_help(*argv); // be valgrind-clean config_destroy(&config_default); config_destroy(&config); exit(EXIT_FAILURE); } } config2options(&config, options); // be valgrind-clean config_destroy(&config_default); config_destroy(&config); if (print_options) options_print(stderr, "", options); }