/* Child process init function that can be called * without cfg_register_child(). * Note that the child process may miss some configuration changes. */ int cfg_late_child_init(void) { /* set the callback list pointer to the beginning of the list */ CFG_LOCK(); atomic_inc(&((*cfg_child_cb_first)->refcnt)); cfg_child_cb = *cfg_child_cb_first; CFG_UNLOCK(); return 0; }
/* per-child process destroy function * Should be called only when the child process exits, * but SER continues running * * WARNING: this function call must be the very last action * before the child process exits, because the local config * is not available afterwards. */ void cfg_child_destroy(void) { cfg_child_cb_t *prev_cb; /* unref the local config */ if (cfg_local) { CFG_UNREF(cfg_local); cfg_local = NULL; } if (!cfg_child_cb || cfg_child_cb==CFG_NO_CHILD_CBS) return; /* The lock must be held to make sure that the global config is not replaced meantime, and the other child processes do not leave the old value of *cfg_child_cb_last. Otherwise it could happen, that all the other processes move their own cfg_child_cb pointer before this process reaches *cfg_child_cb_last, though, it is very unlikely. */ CFG_LOCK(); /* go through the list and check whether there is any item that has to be freed (similar to cfg_update_local(), but without executing the callback functions) */ while (cfg_child_cb != *cfg_child_cb_last) { prev_cb = cfg_child_cb; cfg_child_cb = cfg_child_cb->next; atomic_inc(&cfg_child_cb->refcnt); if (atomic_dec_and_test(&prev_cb->refcnt)) { /* No more pocess refers to this callback. Did this process block the deletion, or is there any other process that has not reached prev_cb yet? */ if (*cfg_child_cb_first == prev_cb) { /* yes, this process was blocking the deletion */ *cfg_child_cb_first = cfg_child_cb; shm_free(prev_cb); } } else { /* no need to continue, because there is at least one process that stays exactly at the same point in the list, so it will free the items later */ break; } } atomic_dec(&cfg_child_cb->refcnt); CFG_UNLOCK(); cfg_child_cb = NULL; }
/* installs a new global config * * replaced is an array of strings that must be freed together * with the previous global config. * cb_first and cb_last define a linked list of per-child process * callbacks. This list is added to the global linked list. */ void cfg_install_global(cfg_block_t *block, void **replaced, cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last) { cfg_block_t* old_cfg; CFG_REF(block); if (replaced) { /* The replaced array is specified, it has to be linked to the child cb structure. * The last child process processing this structure will free the old strings and the array. */ if (cb_first) { cb_first->replaced = replaced; } else { /* At least one child cb structure is needed. */ cb_first = cfg_child_cb_new(NULL, NULL, NULL, 0 /* gname, name, cb, type */); if (cb_first) { cb_last = cb_first; cb_first->replaced = replaced; } else { LOG(L_ERR, "ERROR: cfg_install_global(): not enough shm memory\n"); /* Nothing more can be done here, the replaced strings are still needed, * they cannot be freed at this moment. */ } } } CFG_LOCK(); old_cfg = *cfg_global; *cfg_global = block; if (cb_first) cfg_install_child_cb(cb_first, cb_last); CFG_UNLOCK(); if (old_cfg) CFG_UNREF(old_cfg); }
/* installs a new global config * * replaced is an array of strings that must be freed together * with the previous global config. * cb_first and cb_last define a linked list of per-child process * callbacks. This list is added to the global linked list. */ void cfg_install_global(cfg_block_t *block, char **replaced, cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last) { cfg_block_t* old_cfg; CFG_REF(block); CFG_LOCK(); old_cfg = *cfg_global; *cfg_global = block; if (cb_first) cfg_install_child_cb(cb_first, cb_last); CFG_UNLOCK(); if (old_cfg) { if (replaced) (old_cfg)->replaced = replaced; CFG_UNREF(old_cfg); } }
static void * reload_thread(void *unused) { extern int yynerrs; extern FILE *yyin; FILE *f; struct config_file *new_cfg = NULL, *tmp; struct sigaction signals; /* Initialize signals and start reload thread */ bzero (&signals, sizeof(struct sigaction)); sigemptyset(&signals.sa_mask); sigaddset(&signals.sa_mask, SIGUSR1); signals.sa_handler = sig_usr1_handler; sigaction (SIGUSR1, &signals, NULL); msg_info("reload_thread: starting..."); /* lock on mutex until we got SIGUSR1 that unlocks mutex */ while (1) { pthread_mutex_lock (&cfg_reload_mtx); pthread_cond_wait (&cfg_cond, &cfg_reload_mtx); pthread_mutex_unlock (&cfg_reload_mtx); msg_warn("reload_thread: reloading, rmilter version %s", MVERSION); /* lock for writing */ CFG_WLOCK() ; f = fopen (cfg->cfg_name, "r"); if (f == NULL) { CFG_UNLOCK() ; msg_warn("reload_thread: cannot open file %s, %m", cfg->cfg_name); continue; } new_cfg = (struct config_file*) malloc (sizeof(struct config_file)); if (new_cfg == NULL) { CFG_UNLOCK() ; fclose (f); msg_warn("reload_thread: malloc, %s", strerror (errno)); continue; } bzero (new_cfg, sizeof(struct config_file)); init_defaults (new_cfg); new_cfg->cfg_name = cfg->cfg_name; tmp = cfg; cfg = new_cfg; yyin = f; yyrestart (yyin); if (yyparse () != 0 || yynerrs > 0) { CFG_UNLOCK() ; fclose (f); msg_warn("reload_thread: cannot parse config file %s", cfg->cfg_name); free_config (new_cfg); free (new_cfg); cfg = tmp; continue; } fclose (f); new_cfg->cfg_name = tmp->cfg_name; new_cfg->serial = tmp->serial + 1; /* Strictly set temp dir */ if (!cfg->temp_dir) { msg_warn("tempdir is not set, trying to use $TMPDIR"); cfg->temp_dir = getenv ("TMPDIR"); if (!cfg->temp_dir) { cfg->temp_dir = strdup ("/tmp"); } } #ifdef HAVE_SRANDOMDEV srandomdev(); #else srand (time (NULL)); #endif /* Free old config */ free_config (tmp); free (tmp); CFG_UNLOCK() ; } return NULL; }