cachemgr_change.c revision e1dd0a2f3a26050d1f183c1cafae42c4e3a0b57e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <strings.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <libintl.h>
#include <door.h>
#include <fcntl.h>
#include <procfs.h>
#include "cachemgr.h"
extern admin_t current_admin;
#define CLEANUP_WAIT_TIME 60
typedef enum cleanup_type {
CLEANUP_ALL = 1,
CLEANUP_BY_PID = 2
typedef struct cleanup_op {
} cleanup_op_t;
typedef struct main_nscd_struct {
int in_progress; /* A main nscd thread is */
/* waiting for change or */
/* copying data */
int is_waiting_cleanup; /* A main nscd thread is */
/* waiting for another main */
/* nscd thread to be cleaned */
/* up */
} main_nscd_t;
static main_nscd_t chg_main_nscd = {0, 0, 0, 0};
/*
* The cookie of the configuration and its mutex
*/
static ldap_get_chg_cookie_t config_cookie = {0, 0};
chg_config_cookie_get(void)
{
(void) mutex_lock(&config_cookie_lock);
(void) mutex_unlock(&config_cookie_lock);
return (cookie);
}
static void
{
(void) mutex_lock(&config_cookie_lock);
(void) mutex_unlock(&config_cookie_lock);
}
void
{
(void) mutex_lock(&config_cookie_lock);
(void) mutex_unlock(&config_cookie_lock);
}
static boolean_t
{
return (B_TRUE);
else
return (B_FALSE);
}
/*
* Create a node in the list and output the node. The caller can NOT free it.
*/
static int
{
== NULL) {
logit("waiting_list_add: No memory. pid %ld tid %d\n",
return (CHG_NO_MEMORY);
}
} else {
}
return (CHG_SUCCESS);
}
/*
* Find a node with matching tid in the list and remove it from the list.
*/
static int
{
} else {
}
} else {
} else {
}
}
return (CHG_SUCCESS);
}
}
return (CHG_NOT_FOUND_IN_WAITING_LIST);
}
/*
* Delete the thread from the waiting list and remove data when the list
* is empty.
*/
static void
{
int rc;
chg->chg_wakeup = 0;
}
}
/*
* Set flag by pid so it can be cleaned up.
*/
static void
{
break;
}
}
}
/*
* Return: 1 - door client is dead, 0 - door client is alive
*/
static int
door_client_dead(void)
{
int rc;
rc = 1;
} else {
rc = 0;
}
if (uc)
ucred_free(uc);
return (rc);
}
/*
* This function handles GETSTATUSCHANGE call from main nscd.
* The call can be a START op or STOP op. A cookie is sent from main nscd too.
* The static global variable main_nscd keeps record of pid, tid and some flags.
* If the thread is door_return(), main_nscd.pid, main_nscd.tid are set to 0.
* When the call is START op, it checks if main_nscd.pid is 0. If it is, it
* proceeds to wait for the change notification. If it's not, which means
* another main nscd handling thread is still around. It sends broadcast to
* clean up that thread and wait until the cleanup is done then proceeds to
* wait for the change notification. If same main nscd sends START op
* repeatedly, it'll be rejected.
* It also checks the cookie from main nscd. If it's not the same as
* ldap_cachemgr's cookie, door returns config change.
* If the door call is STOP op, it creates a thread to clean up main nscd START
* thread so it won't be blocking.
* In waiting for the change notification phase, the thread is waken up by
* the notification threads or by the cleanup threads.
* If it's a notification, it copies data to the stack then door return.
* If it's a cleanup, door_client_dead() is called to verify it then
* door return.
*/
int
{
int len, return_now;
(void) mutex_lock(&chg_main_nscd_lock);
if (chg_main_nscd.pid != 0) {
/*
* This is the case that nscd doesn't shut down
* properly(e.g. core) and STOP op is not sent,
* the thread handling it is still around and
* not cleaned up yet.
* Test if the thread is still alive.
* If it is, clean it up.
* For thr_kill, if sig is 0, a validity check
* is done for the existence of the target
* thread; no signal is sent.
*/
}
} else if (chg_main_nscd.in_progress ||
/*
* Same nscd pid can only send door call
* one at a time and wait for ldap_cachemgr to
* return change data. If it's the same pid
* again, it's an nscd error.
*/
(void) mutex_unlock(&chg_main_nscd_lock);
return (CHG_NSCD_REPEATED_CALL);
}
}
/*
* Wait for another thread to be cleaned up if it's alive.
* After that this cond var is waken up.
*/
while (chg_main_nscd.in_progress) {
(void) cond_wait(&chg_main_nscd_cv,
}
}
/*
* Replace pid and tid and set the flag.
*/
(void) mutex_unlock(&chg_main_nscd_lock);
/*
* different cookie, set new cookie and
* return door call right away
*/
len = sizeof (ldap_get_change_out_t);
rc = CHG_NO_MEMORY;
} else {
}
} else {
/* wait for the change notification */
if (rc == CHG_SUCCESS) {
return_now = 0;
while (!chg.chg_wakeup) {
door_client_dead()) {
return_now = 1;
break;
}
}
/* Check if door client is still alive again */
!door_client_dead()) {
/* copy data to buffer */
rc = CHG_NO_MEMORY;
} else {
}
}
}
}
/*
* Reset pid, tid and flag, send wakeup signal.
*/
(void) mutex_lock(&chg_main_nscd_lock);
chg_main_nscd.pid = 0;
chg_main_nscd.tid = 0;
chg_main_nscd.in_progress = 0;
(void) cond_broadcast(&chg_main_nscd_cv);
(void) mutex_unlock(&chg_main_nscd_lock);
rc = CHG_SUCCESS;
} else {
}
if (rc == CHG_EXCEED_MAX_THREADS)
return (rc);
}
/*
* This function copies the header and data stream to the buffer
* then send broadcast to wake up the chg_get_statusChange() threads.
*/
int
chg_notify_statusChange(char *str)
{
}
else
/* NS_STATUS_CHANGE_TYPE_SERVER */
}
return (CHG_SUCCESS);
}
/*
* This is called when the configuration is refreshed.
* The new configuration is different from the current one, a notification
* is sent tochg_get_statusChange() threads.
*/
void
{
int changed = 0;
/*
* Flatten the config data of the newly downloaded config and
* current default config and compare both.
*/
/* error, assume the config is changed */
changed = 1;
!= NULL) {
/* error, assume the config is changed */
changed = 1;
}
if (changed == 0) {
changed = 1;
logit("config changed.\n");
}
}
}
if (changed) {
== NULL) {
logit("chg_test_config_change: No Memory\n");
} else {
/*
* Replace the currentdefault config with the new
* config
*/
/*
* cout->cookie is set by
* chg_notify_statusChange
*/
(void) chg_notify_statusChange((char *)cout);
}
} else {
}
*change_status = changed;
}
/*
* Wake up chg_get_statusChange() threads to clean up the threads
* that main nscd doesn't exist on the other of door anymore or
* the thread is marked as cleanup.
*/
static void
{
if (type == CLEANUP_BY_PID)
/*
* wake up threads without setting chg.chg_wakeup.
* It's for cleanup purpose, not for notifying changes.
*/
}
/*
* If arg is NULL, it loops forever,
* else it calls cleanup_threads once and exits.
*/
void *
chg_cleanup_waiting_threads(void *arg)
{
cleanup_type_t type = 0;
waiting = 1;
type = CLEANUP_ALL;
pid = 0;
} else {
waiting = 0;
}
while (always) {
if (waiting)
(void) sleep(CLEANUP_WAIT_TIME);
if (!waiting)
break;
}
if (op)
return (NULL);
}
/*
* The door server thead which has the door client pid will be marked
* as to be clean up. If pid is 0, no marking and just clean up all.
*/
static void
{
return;
/* clean up all if pid is 0 */
if (pid == 0)
else
logit("thr_create failed for cleanup_thread_by_pid(%ld)\n",
pid);
}
}
/*
* Output a psinfo of an nscd process with process id pid
* Return: 0 - Can't find the process or it's not nscd
* 1 - psinfo found
* Note: If info is NULL, returns 0 or 1 only and no output from info.
*/
static int
{
char fname[MAXPATHLEN];
int fd;
if (info)
return (1);
}
}
}
return (0);
}
/*
* If the parent process is nscd and euid is 0, it's a peruser nscd.
*/
static int
{
/*
* get psinfo of parent forker nscd
*/
return (1);
else
return (0);
} else {
return (0);
}
}
/*
* Check if the door client making door call is a nscd or peruser nscd and
* output door client's pid.
*/
int
{
int rc;
if (door_ucred(&uc) != 0) {
return (0);
}
is_peruser_nscd(pid)) {
logit("ldap_cachemgr received %s call from pid %ld, "
rc = 1;
} else {
logit("%s call failed(cred): caller pid %ld, uid %u, "
rc = 0;
}
ucred_free(uc);
return (rc);
}