/* 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); } }