/* * pvm_parser.c * * Copyright (C) Malanchini Marzio - August 2003 * Updates and port to new libtc configuration file parser: * (C) 2007 Francesco Romani * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * OK, I must say that I'm not very proud of my work done here. * I think I'll must improve it soon. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "libtc/libtc.h" #include "libtc/tclist.h" #include "libtc/cfgfile.h" #include #include #include #include #include /*************************************************************************/ /* helpers */ #define LOCALHOST "." /* XXX ?! sic -- Fromani */ static char *pvm_hostname(char *candidate) { if (candidate) { tc_strstrip(candidate); if (strlen(candidate) > 0) { // XXX enlarge check value return candidate; } } return LOCALHOST; } /* commodity */ typedef char PVMString[TC_BUF_MIN]; typedef struct pvmnodedata_ PVMNodeData; struct pvmnodedata_ { PVMString hostname; int enabled; /* flag */ int maxprocs; }; /*************************************************************************/ /* main data structure */ static pvm_config_env s_pvm_conf; /*************************************************************************/ /* forward declarations of dispatchers */ static pvm_config_filelist *dispatch_list(TCList *src, int type, char *codec, char *destination, pvm_config_filelist **ret); static int dispatch_node(int id, PVMNodeData *data, pvm_config_env *env); static int dispatch_null(int id, pvm_config_env *env); static int dispatch_merger(int id, pvm_config_env *env); static int dispatch_modules(int id, pvm_config_env *env); static int dispatch_syslist(int id, pvm_config_env *env); /*************************************************************************/ /* * since configuration uses a lot of strings, we need a lot of temporary * buffers to store them */ /* Nodes */ static int nodes_num = 1; /* this is pretty ugly, isn't it? */ static PVMNodeData nodes_data[PVM_MAX_NODES]; /* SystemMerger */ static PVMString systemmerger_hostname; static PVMString systemmerger_mplexparams; /* AudioMerger */ static PVMString audiomerger_hostname; /* VideoMerger */ static PVMString videomerger_hostname; /* ExportAudioModule */ static PVMString exportaudiomod_codec; static PVMString exportaudiomod_params[PVM_MAX_CODEC_PARAMS]; /* ExportVideoModule */ static PVMString exportvideomod_codec; static PVMString exportvideomod_params[PVM_MAX_CODEC_PARAMS]; /* SystemList */ static PVMString systemlist_codec; static PVMString systemlist_destination; static PVMString systemlist_mplexparams; /* AddAudio */ static PVMString addaudio_codec; static PVMString addaudio_destination; /* AddVideo */ static PVMString addvideo_codec; static PVMString addvideo_destination; /*************************************************************************/ #define NODEINIT(ID) \ { { "Hostname", (nodes_data[ID].hostname), TCCONF_TYPE_STRING, 0, 0, 0 }, \ { "NumProcMax", &(nodes_data[ID].maxprocs), TCCONF_TYPE_INT, \ 0, 1, PVM_MAX_NODE_PROCS }, \ { "Enabled", &(nodes_data[ID].enabled), TCCONF_TYPE_FLAG, 0, 0, 1 }, \ { NULL, NULL, 0, 0, 0, 0, } } static TCConfigEntry node_conf[PVM_MAX_NODES][4] = { NODEINIT(0), NODEINIT(1), NODEINIT(2), NODEINIT(3), NODEINIT(4), NODEINIT(5), NODEINIT(6), NODEINIT(7), }; #undef NODEINIT static TCConfigEntry pvmhostcaps_conf[] = { { "NumProcMaxForHost", &(s_pvm_conf.s_nproc), TCCONF_TYPE_INT, 0, 1, PVM_NUM_NODE_PROCS }, { "MaxProcForCluster", &(s_pvm_conf.s_max_proc), TCCONF_TYPE_INT, 0, 1, PVM_MAX_CLUSTER_PROCS }, { "NumElabFrameForTask", &(s_pvm_conf.s_num_frame_task), TCCONF_TYPE_INT, 0, 1, PVM_NUM_TASK_FRAMES }, { "InternalMultipass", &(s_pvm_conf.s_internal_multipass), TCCONF_TYPE_FLAG, 0, 0, 1 }, { "Nodes", &nodes_num, TCCONF_TYPE_INT, 0, 1, PVM_MAX_NODES }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry videomerger_conf[] = { { "Hostname", videomerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 }, { "BuildOnlyBatchMergeList", &(s_pvm_conf.s_video_merger.s_build_only_list), TCCONF_TYPE_FLAG, 0, 0, 1 }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry audiomerger_conf[] = { { "Hostname", audiomerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 }, { "BuildOnlyBatchMergeList", &(s_pvm_conf.s_audio_merger.s_build_only_list), TCCONF_TYPE_FLAG, 0, 0, 1 }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry systemmerger_conf[] = { { "Hostname", systemmerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 }, { "BuildOnlyBatchMergeList", &(s_pvm_conf.s_system_merger.s_build_only_list), TCCONF_TYPE_FLAG, 0, 0, 1 }, { "MultiplexParams", systemmerger_mplexparams, TCCONF_TYPE_STRING, 0, 0, }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry exportaudiomod_conf[] = { { "Codec", exportaudiomod_codec, TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param1", exportaudiomod_params[0], TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param2", exportaudiomod_params[1], TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param3", exportaudiomod_params[2], TCCONF_TYPE_STRING, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry exportvideomod_conf[] = { { "Codec", exportvideomod_codec, TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param1", exportvideomod_params[0], TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param2", exportvideomod_params[1], TCCONF_TYPE_STRING, 0, 0, 0 }, { "Param3", exportvideomod_params[2], TCCONF_TYPE_STRING, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry systemlist_conf[] = { { "Destination", systemlist_destination, TCCONF_TYPE_STRING, 0, 0, 0 }, { "Codec", systemlist_codec, TCCONF_TYPE_STRING, 0, 0, 0 }, { "BuildOnlyBatchMergeList", &(s_pvm_conf.s_build_intermed_file), TCCONF_TYPE_FLAG, 0, 0, 1 }, { "MultiplexParams", systemlist_mplexparams, TCCONF_TYPE_STRING, 0, 0, }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry addaudio_conf[] = { { "Destination", addaudio_destination, TCCONF_TYPE_STRING, 0, 0, 0 }, { "Codec", addaudio_codec, TCCONF_TYPE_STRING, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0, }, }; static TCConfigEntry addvideo_conf[] = { { "Destination", addvideo_destination, TCCONF_TYPE_STRING, 0, 0, 0 }, { "Codec", addvideo_codec, TCCONF_TYPE_STRING, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0, }, }; /*************************************************************************/ typedef struct pvm_conf_item_ PVMConfItem; struct pvm_conf_item_ { const char *name; TCConfigEntry *conf; int (*dispatch)(int id, pvm_config_env *env); int serverside; /* flag */ int parsed; }; /* BIGFAT WARNING: please take a great care to keep in sync */ enum config_idx { CONF_PVM_HOST_CAPS_IDX = 0, CONF_AUDIO_MERGER_IDX, CONF_VIDEO_MERGER_IDX, CONF_SYSTEM_MERGER_IDX, CONF_EXPORT_AUDIO_MOD_IDX, CONF_EXPORT_VIDEO_MOD_IDX, CONF_SYSTEM_LIST_IDX, CONF_ADD_AUDIO_IDX, CONF_ADD_VIDEO_IDX, }; static PVMConfItem pvm_config[] = { { "PvmHostCapability", pvmhostcaps_conf, dispatch_null, 0, 0, }, { "AudioMerger", audiomerger_conf, dispatch_merger, 0, 0, }, { "VideoMerger", videomerger_conf, dispatch_merger, 0, 0, }, { "SystemMerger", systemmerger_conf, dispatch_merger, 0, 0, }, { "ExportAudioModule", exportaudiomod_conf, dispatch_modules, 0, 0, }, { "ExportVideoModule", exportvideomod_conf, dispatch_modules, 0, 0, }, { "SystemList", systemlist_conf, dispatch_syslist, 1, 0, }, { "AddAudio", addaudio_conf, dispatch_null, 1, 0, }, { "AddVideo", addvideo_conf, dispatch_null, 1, 0, }, { NULL, NULL, NULL, 0, 0, }, }; typedef struct pvm_list_item_ PVMListItem; struct pvm_list_item_ { const char *name; pvm_config_filelist **list; int type; int parsed; /* dispatcher can be generic, so no need for specific one */ }; static PVMListItem pvm_filelist[] = { { "AddAudioList", &s_pvm_conf.p_add_list, TC_AUDIO, 0, }, { "AddVideoList", &s_pvm_conf.p_add_list, TC_VIDEO, 0, }, { "LogAudioList", &s_pvm_conf.p_add_loglist, TC_AUDIO, 0, }, { "LogVideoList", &s_pvm_conf.p_add_loglist, TC_VIDEO, 0, }, { "RemoveAudioList", &s_pvm_conf.p_rem_list, TC_AUDIO, 0, }, { "RemoveVideoList", &s_pvm_conf.p_rem_list, TC_VIDEO, 0, }, { NULL, NULL, 0, 0, }, }; /*************************************************************************/ /* dispatcher functions */ /* * yes, that's ugly (and I don't really wnat expend too much on this). * Improvements are warmly welcome, but a rewrite from scratch is * probably the way to go. */ struct dispatch_data { pvm_config_filelist *head; pvm_config_filelist *tail; char *codec; char *destination; int type; }; static int dispatch_list_item(TCListItem *item, void *userdata) { struct dispatch_data *DD = userdata; int ret = 0; pvm_config_filelist *cur = tc_zalloc(sizeof(pvm_config_filelist)); // XXX if (!cur) { ret = 1; } else { cur->s_type = DD->type; cur->p_codec = DD->codec; // softref cur->p_destination = DD->destination; // softref cur->p_filename = item->data; // hardref if (!DD->head) { DD->head = cur; DD->tail = cur; } else { DD->tail->p_next = cur; cur = DD->tail->p_next; } } return ret; } static pvm_config_filelist *dispatch_list(TCList *src, int type, char *codec, char *destination, pvm_config_filelist **ret) { struct dispatch_data DD = { .head = NULL, .tail = NULL, .type = type, .codec = codec, .destination = destination, }; tc_list_foreach(src, dispatch_list_item, &DD); if (ret) { *ret = DD.tail; } return DD.head; } static int dispatch_node(int id, PVMNodeData *data, pvm_config_env *env) { /* * this insert nodes in reverse orders, so node defined last * in configuration file is first on list, but nobody really * cares about that. */ if (env && data && data->enabled) { pvm_config_hosts *host = tc_zalloc(sizeof(pvm_config_hosts)); if (host) { /* fill */ host->p_hostname = data->hostname; host->s_nproc = data->maxprocs; /* link */ host->p_next = env->p_pvm_hosts; env->p_pvm_hosts = host; return 1; } } return 0; } static int dispatch_merger(int id, pvm_config_env *env) { switch (id) { case CONF_AUDIO_MERGER_IDX: env->s_audio_merger.p_hostname = pvm_hostname(audiomerger_hostname); return 1; case CONF_VIDEO_MERGER_IDX: env->s_video_merger.p_hostname = pvm_hostname(videomerger_hostname); return 1; case CONF_SYSTEM_MERGER_IDX: env->s_system_merger.p_hostname = pvm_hostname(systemmerger_hostname); tc_strstrip(systemmerger_mplexparams); env->p_multiplex_cmd = systemmerger_mplexparams; return 1; default: /* cannot happen */ return 0; } return 0; /* paranoia */ } static int dispatch_null(int id, pvm_config_env *env) { return (env != NULL) ?1 :0; } static int dispatch_modules(int id, pvm_config_env *env) { pvm_config_codec *cfg = NULL; char *codec = NULL; PVMString *params = NULL; switch (id) { case CONF_EXPORT_AUDIO_MOD_IDX: cfg = &(env->s_audio_codec); codec = exportaudiomod_codec; params = exportaudiomod_params; break; case CONF_EXPORT_VIDEO_MOD_IDX: cfg = &(env->s_video_codec); codec = exportvideomod_codec; params = exportvideomod_params; break; default: /* cannot happen */ return 0; } tc_strstrip(codec); tc_strstrip(params[0]); tc_strstrip(params[1]); tc_strstrip(params[2]); cfg->p_codec = codec; cfg->p_par1 = params[0]; cfg->p_par2 = params[1]; cfg->p_par3 = params[2]; return 1; } static int dispatch_syslist(int id, pvm_config_env *env) { if (env != NULL) { tc_strstrip(systemlist_codec); tc_strstrip(systemlist_destination); tc_strstrip(systemlist_mplexparams); env->s_sys_list.p_codec = systemlist_codec; env->s_sys_list.p_destination = systemlist_destination; env->p_multiplex_cmd = systemlist_mplexparams; return 1; } return 0; } /*************************************************************************/ static int parse_nodes(char *p_hostfile, int nodes) { int i = 0, ret = 0, parsed = 0; char buf[TC_BUF_LINE]; if (nodes > PVM_MAX_NODES) { tc_log_warn(__FILE__, "excessive nodes requested, autolimit to %i", PVM_MAX_NODES); nodes = PVM_MAX_NODES; } for (i = 0; i < nodes; i++) { tc_snprintf(buf, sizeof(buf), "Node%i", i+1); ret = module_read_config(p_hostfile, buf, node_conf[i], __FILE__); if (ret) { int done = dispatch_node(i, &nodes_data[i], &s_pvm_conf); if (done) { parsed++; } } } return parsed; } static void parse_config(char *p_hostfile, int full) { int i = 0; for (i = 0; pvm_config[i].name != NULL; i++) { int ret = 0; if (!full && pvm_config[i].serverside) continue; ret = module_read_config(p_hostfile, pvm_config[i].name, pvm_config[i].conf, __FILE__); if (ret) { int done = pvm_config[i].dispatch(i, &s_pvm_conf); pvm_config[i].parsed = done; } } } static void parse_filelist(char *p_hostfile) { int i = 0; for (i = 0; pvm_filelist[i].name != NULL; i++) { TCList *list = module_read_config_list(p_hostfile, pvm_filelist[i].name, __FILE__); if (list) { int type = pvm_filelist[i].type; char *codec = (type == TC_VIDEO) ?addvideo_codec :addaudio_codec; char *dest = (type == TC_VIDEO) ?addvideo_destination :addaudio_destination; pvm_config_filelist *tail = NULL; pvm_config_filelist *head = dispatch_list(list, type, codec, dest, &tail); if (head) { /* then tail is valid too */ pvm_filelist[i].parsed = 1; if (*(pvm_filelist[i].list) != NULL) { tail->p_next = *(pvm_filelist[i].list); } *(pvm_filelist[i].list) = head; } /* always */ module_free_config_list(list, (head != NULL) ?1 :0); } } } #define WAS_PARSED(IDX) pvm_config[(IDX)].parsed static pvm_config_env *validate(int nodes, int verbose) { const char *errmsg = "???"; if (nodes < 0) { errmsg = "Need one PVM node configured"; goto failed; } if (((!s_pvm_conf.s_audio_codec.p_codec) && WAS_PARSED(CONF_EXPORT_AUDIO_MOD_IDX)) || ((!s_pvm_conf.s_video_codec.p_codec) && WAS_PARSED(CONF_EXPORT_VIDEO_MOD_IDX))) { errmsg = "Need at least Codec parameter in the" " [ExportVideoModule] or [ExportAudioModule] section"; goto failed; } if ((s_pvm_conf.s_system_merger.p_hostname != NULL) && (s_pvm_conf.p_multiplex_cmd == NULL)) { errmsg = "MultiplexParams parameter required in the" " [SystemMerger] section"; goto failed; } else if (s_pvm_conf.s_system_merger.p_hostname != NULL) { s_pvm_conf.s_video_merger.s_build_only_list = 1; s_pvm_conf.s_audio_merger.s_build_only_list = 1; } if ((s_pvm_conf.p_add_list != NULL) && (s_pvm_conf.p_add_list->p_codec == NULL) && (WAS_PARSED(CONF_ADD_AUDIO_IDX) || WAS_PARSED(CONF_ADD_VIDEO_IDX))) { errmsg = "Need at least Codec parameter in the [AddList] section"; goto failed; } /* done */ return &s_pvm_conf; failed: if (verbose) { tc_log_error(__FILE__, "%s", errmsg); } pvm_parser_close(); return NULL; } #undef WAS_PARSED pvm_config_env *pvm_parser_open(char *p_hostfile, int verbose, int full) { int i = 0; /* setup defaults */ s_pvm_conf.p_pvm_hosts = NULL; /* XXX: add more defaults? */ /* get user data */ parse_config(p_hostfile, full); /* get node data */ i = parse_nodes(p_hostfile, nodes_num); /* get lists */ if (full) { parse_filelist(p_hostfile); } /* then validate it */ return validate(i, verbose); } void pvm_parser_close(void) { pvm_config_hosts *p_pvm_conf_host = NULL, *p_tmp = NULL; pvm_config_filelist *p_pvm_conf_fileadd = NULL; pvm_config_filelist *p_pvm_conf_filerem = NULL; for (p_pvm_conf_host = s_pvm_conf.p_pvm_hosts; p_pvm_conf_host != NULL; ) { p_tmp = p_pvm_conf_host->p_next; free(p_pvm_conf_host); p_pvm_conf_host = p_tmp; } for (p_pvm_conf_fileadd = s_pvm_conf.p_add_list; p_pvm_conf_fileadd != NULL; ) { p_pvm_conf_filerem = p_pvm_conf_fileadd->p_next; free(p_pvm_conf_fileadd); p_pvm_conf_fileadd = p_pvm_conf_filerem; } for (p_pvm_conf_fileadd = s_pvm_conf.p_rem_list; p_pvm_conf_fileadd != NULL; ) { p_pvm_conf_filerem = p_pvm_conf_fileadd->p_next; free(p_pvm_conf_fileadd); p_pvm_conf_fileadd = p_pvm_conf_filerem; } memset(&s_pvm_conf, 0, sizeof(s_pvm_conf)); } /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */