/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */
/*
* Copyright Likewise Software 2004-2008
* All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library 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 Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
#include "domainjoin.h"
#include "ctshell.h"
#include "djdistroinfo.h"
#include "djdaemonmgr.h"
#define GCE(x) GOTO_CLEANUP_ON_CENTERROR((x))
static PSTR pszChkConfigPath = "/sbin/chkconfig";
static PSTR pszUpdateRcDFilePath = "/usr/sbin/update-rc.d";
#define DAEMON_BINARY_SEARCH_PATH "/usr/local/sbin:/usr/local/bin:/usr/dt/bin:/opt/dce/sbin:/usr/sbin:/usr/bin:/sbin:/bin:" BINDIR ":" SBINDIR
CENTERROR
GetInitScriptDir(PSTR *store)
{
#if defined(_AIX)
return CTStrdup("/etc/rc.d/init.d", store);
#elif defined(_HPUX_SOURCE)
return CTStrdup("/sbin/init.d", store);
#elif defined(__LWI_FREEBSD__)
return CTStrdup("/etc/rc.d", store);
#else
return CTStrdup("/etc/init.d", store);
#endif
}
static void FindDaemonScript(PCSTR name, PSTR *path, LWException **exc);
void
DJGetDaemonStatus(
PCSTR pszDaemonName,
PBOOLEAN pbStarted,
LWException **exc
)
{
PSTR statusBuffer = NULL;
PSTR prefixedPath = NULL;
PSTR initDir = NULL;
PSTR daemonPath = NULL;
PSTR altDaemonName = NULL;
long status = 0;
PPROCINFO pProcInfo = NULL;
BOOLEAN daemon_installed = FALSE;
CENTERROR ceError;
if(pszDaemonName[0] == '/')
LW_CLEANUP_CTERR(exc, CTStrdup(pszDaemonName, &prefixedPath));
else
{
LW_TRY(exc, FindDaemonScript(pszDaemonName, &prefixedPath, &LW_EXC));
}
DJ_LOG_INFO("Checking status of daemon [%s]", prefixedPath);
LW_CLEANUP_CTERR(exc, CTCheckFileExists(prefixedPath, &daemon_installed));
if (!daemon_installed) {
LW_CLEANUP_CTERR(exc, CENTERROR_DOMAINJOIN_MISSING_DAEMON);
}
/* AIX has /etc/rc.dt. When '/etc/rc.dt status' is run, it tries to
* start XWindows. So to be safe, we won't call that.
*/
if(!strcmp(prefixedPath, "/etc/rc.dt"))
{
status = 1;
}
else if(!strcmp(prefixedPath, "/sbin/init.d/Rpcd"))
{
/* HP-UX has this script. When it is run with the status option, it
* prints out the usage info and leaves with exit code 0.
*/
status = 1;
}
else
{
LW_CLEANUP_CTERR(exc, CTShell("%script status >/dev/null 2>&1; echo $? >%statusBuffer",
CTSHELL_STRING(script, prefixedPath),
CTSHELL_BUFFER(statusBuffer, &statusBuffer)));
status = atol(statusBuffer);
// see
// http://forgeftp.novell.com/library/SUSE%20Package%20Conventions/spc_init_scripts.html
// and http://www.linuxbase.org/~gk4/wip-sys-init.html
//
// note further that some other error codes might be thrown, so
// we choose to only pay attention to the ones that lsb says are
// valid return codes for status that indicate that a progam
// isnt running, otherwise, we are gonna throw it.
DJ_LOG_INFO("Daemon [%s]: status [%d]", prefixedPath, status);
}
if (!status) {
*pbStarted = TRUE;
}
else if (status == 2 || status == 3 || status == 4)
*pbStarted = FALSE;
else if (status == 1)
{
// An unknown error occurred. Most likely the init script doesn't
// support the query option. We'll have to query it with ps.
pid_t daemonPid;
const char *daemonBaseName = strrchr(pszDaemonName, '/');
if(daemonBaseName == NULL)
{
daemonBaseName = pszDaemonName;
}
else
{
daemonBaseName++;
}
DJ_LOG_VERBOSE("Looking for %s", daemonBaseName);
if(!strcmp(daemonBaseName, "likewise-open"))
daemonBaseName = "likewise-winbindd";
ceError = CTFindFileInPath(daemonBaseName,
DAEMON_BINARY_SEARCH_PATH, &daemonPath);
if(ceError == CENTERROR_FILE_NOT_FOUND)
{
CT_SAFE_FREE_STRING(altDaemonName);
LW_CLEANUP_CTERR(exc, CTStrdup(daemonBaseName, &altDaemonName));
CTStrToLower(altDaemonName);
ceError = CTFindFileInPath(altDaemonName,
DAEMON_BINARY_SEARCH_PATH, &daemonPath);
}
if(ceError == CENTERROR_FILE_NOT_FOUND)
{
CT_SAFE_FREE_STRING(altDaemonName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&altDaemonName,
"%sd", daemonBaseName));
ceError = CTFindFileInPath(altDaemonName,
DAEMON_BINARY_SEARCH_PATH, &daemonPath);
}
if(ceError == CENTERROR_FILE_NOT_FOUND)
{
LW_RAISE_EX(exc, CENTERROR_FILE_NOT_FOUND, "Cannot find daemon binary", "Querying the daemon init script with '%s status' returned code 1. This either means the init script does not support the status option, or the daemon died leaving a pid file. This program is instead attempting to see if the program is running using the ps command, however the daemon binary (%s) that goes along with the init script cannot be found.", prefixedPath, daemonBaseName);
goto cleanup;
}
LW_CLEANUP_CTERR(exc, ceError);
DJ_LOG_VERBOSE("Found %s", daemonPath);
ceError = CTGetPidOfCmdLine(NULL, daemonPath, NULL, 0, &daemonPid, NULL);
if(ceError == CENTERROR_NO_SUCH_PROCESS || ceError == CENTERROR_NOT_IMPLEMENTED)
{
//Nope, couldn't find the daemon running
*pbStarted = FALSE;
}
else
{
LW_CLEANUP_CTERR(exc, ceError);
DJ_LOG_INFO("Even though '%s status' exited with code 1, '%s' is running with pid %d.", prefixedPath, daemonPath, daemonPid);
*pbStarted = TRUE;
}
}
else {
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_UNEXPECTED_ERRCODE, "Non-standard return code from init script", "According to http://forgeftp.novell.com/library/SUSE%20Package%20Conventions/spc_init_scripts.html, init scripts should return 0, 1, 2, 3, or 4. However, '%s status' returned %d.", status, prefixedPath);
goto cleanup;
}
cleanup:
if (pProcInfo)
FreeProcInfo(pProcInfo);
CT_SAFE_FREE_STRING(prefixedPath);
CT_SAFE_FREE_STRING(initDir);
CT_SAFE_FREE_STRING(daemonPath);
CT_SAFE_FREE_STRING(altDaemonName);
CT_SAFE_FREE_STRING(statusBuffer);
}
static void FindDaemonScript(PCSTR name, PSTR *path, LWException **exc)
{
PSTR altName = NULL;
CENTERROR ceError;
const char *searchPath = "/etc/init.d:/etc/rc.d/init.d:/sbin/init.d:/etc/rc.d";
BOOLEAN fileExists = FALSE;
*path = NULL;
if (name[0] == '/')
{
LW_CLEANUP_CTERR(exc, CTCheckFileOrLinkExists(name, &fileExists));
if (!fileExists)
{
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_MISSING_DAEMON,
"Unable to find daemon",
"The '%s' daemon could not be found.",
name);
}
LW_CLEANUP_CTERR(exc, CTAllocateString(name, path));
}
else
{
ceError = CTFindFileInPath(name, searchPath, path);
if(ceError == CENTERROR_FILE_NOT_FOUND)
{
CT_SAFE_FREE_STRING(altName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&altName,
"%s.rc", name));
ceError = CTFindFileInPath(altName, searchPath, path);
}
if(ceError == CENTERROR_FILE_NOT_FOUND && !strcmp(name, "dtlogin"))
{
CT_SAFE_FREE_STRING(altName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&altName,
"rc.dt"));
ceError = CTFindFileInPath(altName, searchPath, path);
}
if(ceError == CENTERROR_FILE_NOT_FOUND)
{
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_MISSING_DAEMON,
"Unable to find daemon",
"The '%s' daemon could not be found in the search path '%s'. It could not be found with the alternative name '%s' either.",
name, searchPath, altName);
goto cleanup;
}
LW_CLEANUP_CTERR(exc, ceError);
}
cleanup:
CT_SAFE_FREE_STRING(altName);
}
void
DJStartStopDaemon(
PCSTR pszDaemonName,
BOOLEAN bStatus,
LWException **exc
)
{
PSTR* ppszArgs = NULL;
DWORD nArgs = 4;
long status = 0;
PPROCINFO pProcInfo = NULL;
BOOLEAN bStarted = FALSE;
PSTR pszDaemonPath = NULL;
int retry;
LW_TRY(exc, FindDaemonScript(pszDaemonName, &pszDaemonPath, &LW_EXC));
if (bStatus) {
DJ_LOG_INFO("Starting daemon [%s]", pszDaemonPath);
} else {
DJ_LOG_INFO("Stopping daemon [%s]", pszDaemonPath);
}
if (!strcmp(pszDaemonPath, "/etc/rc.dt") && !bStatus)
{
// The dtlogin init script is broken on AIX. It always starts the
// process.
LW_CLEANUP_CTERR(exc, CTAllocateMemory(sizeof(PSTR)*nArgs, PPCAST(&ppszArgs)));
LW_CLEANUP_CTERR(exc, CTAllocateString("/bin/sh", ppszArgs));
LW_CLEANUP_CTERR(exc, CTAllocateString("-c", ppszArgs+1));
LW_CLEANUP_CTERR(exc, CTAllocateString("kill `cat /var/dt/Xpid`", ppszArgs+2));
}
else {
LW_CLEANUP_CTERR(exc, CTAllocateMemory(sizeof(PSTR)*nArgs, PPCAST(&ppszArgs)));
LW_CLEANUP_CTERR(exc, CTAllocateString(pszDaemonPath, ppszArgs));
LW_CLEANUP_CTERR(exc, CTAllocateString((bStatus ? "start" : "stop"), ppszArgs+1));
}
LW_CLEANUP_CTERR(exc, DJSpawnProcess(ppszArgs[0], ppszArgs, &pProcInfo));
LW_CLEANUP_CTERR(exc, DJGetProcessStatus(pProcInfo, &status));
for(retry = 0; retry < 20; retry++)
{
LW_TRY(exc, DJGetDaemonStatus(pszDaemonName, &bStarted, &LW_EXC));
if (bStarted == bStatus)
break;
sleep(1);
}
if (bStarted != bStatus) {
if(bStatus)
{
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_INCORRECT_STATUS, "Unable to start daemon", "An attempt was made to start the '%s' daemon, but querying its status revealed that it did not start. Try running '%s start; %s status' to diagnose the issue", pszDaemonPath, pszDaemonPath, pszDaemonPath);
}
else
{
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_INCORRECT_STATUS, "Unable to stop daemon", "An attempt was made to stop the '%s' daemon, but querying its status revealed that it did not stop. Try running '%s stop; %s status' to diagnose the issue", pszDaemonPath, pszDaemonPath, pszDaemonPath);
}
goto cleanup;
}
cleanup:
if (ppszArgs)
CTFreeStringArray(ppszArgs, nArgs);
if (pProcInfo)
FreeProcInfo(pProcInfo);
CT_SAFE_FREE_STRING(pszDaemonPath);
}
void
DJDoUpdateRcD(
PCSTR pszDaemonName,
BOOLEAN bStatus,
int startPriority,
int stopPriority,
LWException **exc
)
{
PSTR command = NULL;
if (!bStatus) {
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&command,
"%s -f %s remove", pszUpdateRcDFilePath,
pszDaemonName));
}
else
{
if (startPriority == 0)
startPriority = 20;
if (stopPriority == 0)
stopPriority = 20;
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&command,
"%s %s defaults %d %d", pszUpdateRcDFilePath,
pszDaemonName, startPriority, stopPriority));
}
CTCaptureOutputToExc(command, exc);
if(exc != NULL && *exc != NULL &&
(*exc)->code == CENTERROR_COMMAND_FAILED)
{
// Put in a more specific error code
(*exc)->code = CENTERROR_DOMAINJOIN_UPDATERCD_FAILED;
}
cleanup:
CT_SAFE_FREE_STRING(command);
}
CENTERROR
DJDoChkConfig(
PCSTR pszDaemonName,
BOOLEAN bStatus
)
{
CENTERROR ceError = CENTERROR_SUCCESS;
CHAR szDaemonPath[PATH_MAX+1];
PSTR* ppszArgs = NULL;
DWORD nArgs = 4;
LONG status = 0;
PPROCINFO pProcInfo = NULL;
FILE* fp = NULL;
CHAR szBuf[1024+1];
#if defined(_AIX)
sprintf(szDaemonPath, "/etc/rc.d/init.d/%s", pszDaemonName);
#elif defined(_HPUX_SOURCE)
sprintf(szDaemonPath, "/sbin/init.d/%s", pszDaemonName);
#elif defined(__LWI_FREEBSD__)
sprintf(szDaemonPath, "/etc/rc.d/%s", pszDaemonName);
#else
sprintf(szDaemonPath, "/etc/init.d/%s", pszDaemonName);
#endif
// if we got this far, we have set the daemon to the running state
// that the caller has requested. now we need to set it's init
// state (whether or not it get's ran on startup) to what the
// caller has requested. we can do this unconditionally because
// chkconfig wont complain if we do an --add to something that is
// already in the --add state.
ceError = CTAllocateMemory(sizeof(PSTR)*nArgs, PPCAST(&ppszArgs));
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = CTAllocateString(pszChkConfigPath, ppszArgs);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = CTAllocateString((bStatus ? "--add" : "--del"), ppszArgs+1);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = CTAllocateString(pszDaemonName, ppszArgs+2);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = DJSpawnProcess(ppszArgs[0], ppszArgs, &pProcInfo);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = DJGetProcessStatus(pProcInfo, &status);
BAIL_ON_CENTERIS_ERROR(ceError);
if (status != 0) {
ceError = CENTERROR_DOMAINJOIN_CHKCONFIG_FAILED;
BAIL_ON_CENTERIS_ERROR(ceError);
}
// this step is needed in some circumstances, but not all. i
// *think* that this step is needed for LSB-1.2 only. furthermore
// this step appears to turn off LSB-2.0 daemons, so we have to
// make sure that we dont try to run it on them. so we check for
// BEGIN INIT INFO. It's slower than i would like due to the need
// to read the file in and do stringops on it. shelling out and
// using grep *might* be faster, but it might not be, and it has
// all the complexity associated with running native apps.
fp = fopen(szDaemonPath, "r");
if (fp == NULL) {
ceError = CTMapSystemError(errno);
BAIL_ON_CENTERIS_ERROR(ceError);
}
while (1) {
if (fgets(szBuf, 1024, fp) == NULL) {
if (feof(fp))
break;
else {
ceError = CTMapSystemError(errno);
BAIL_ON_CENTERIS_ERROR(ceError);
}
if (strstr(szBuf, "chkconfig:")) {
CTFreeString(*(ppszArgs+1));
if (pProcInfo) {
FreeProcInfo(pProcInfo);
pProcInfo = NULL;
}
ceError = CTAllocateString((bStatus ? "on" : "off"), ppszArgs+1);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = DJSpawnProcess(ppszArgs[0], ppszArgs, &pProcInfo);
BAIL_ON_CENTERIS_ERROR(ceError);
ceError = DJGetProcessStatus(pProcInfo, &status);
BAIL_ON_CENTERIS_ERROR(ceError);
if (status != 0) {
ceError = CENTERROR_DOMAINJOIN_CHKCONFIG_FAILED;
BAIL_ON_CENTERIS_ERROR(ceError);
}
}
if (strstr(szBuf, "BEGIN INIT INFO"))
break;
}
}
error:
if (fp)
fclose(fp);
if (pProcInfo) {
FreeProcInfo(pProcInfo);
pProcInfo = NULL;
}
if (ppszArgs)
CTFreeStringArray(ppszArgs, nArgs);
return ceError;
}
void
DJOverwriteSymlink(
PCSTR symlinkTarget,
PCSTR symlinkName,
LWException **exc
)
{
BOOLEAN bFileExists = FALSE;
DJ_LOG_INFO("Creating symlink [%s] -> [%s]", symlinkName, symlinkTarget);
LW_CLEANUP_CTERR(exc, CTCheckFileOrLinkExists(symlinkName, &bFileExists));
if (bFileExists)
{
LW_CLEANUP_CTERR(exc, CTRemoveFile(symlinkName));
}
LW_CLEANUP_CTERR(exc, CTCreateSymLink(symlinkTarget,
symlinkName));
cleanup:
;
}
void
DJRemoveDaemonLinksInDirectories(
PCSTR *directories,
PCSTR daemonName,
LWException **exc
)
{
PSTR searchExpression = NULL;
PSTR *matchingPaths = NULL;
int i, j;
DWORD matchCount = 0;
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&searchExpression,
"^.*%s$", daemonName));
for(i = 0; directories[i] != NULL; i++)
{
LW_CLEANUP_CTERR(exc, CTGetMatchingFilePathsInFolder(
directories[i], searchExpression, &matchingPaths,
&matchCount));
for(j = 0; j < matchCount; j++)
{
DJ_LOG_INFO("Removing init script symlink [%s]",
matchingPaths[j]);
LW_CLEANUP_CTERR(exc, CTRemoveFile(matchingPaths[j]));
}
//CTFreeStringArray will ignore matchingPaths if it is NULL
CTFreeStringArray(matchingPaths, matchCount);
matchingPaths = NULL;
}
cleanup:
CTFreeStringArray(matchingPaths, matchCount);
CT_SAFE_FREE_STRING(searchExpression);
}
void
DJConfigureForDaemonRestart(
PCSTR pszDaemonName,
BOOLEAN bStatus,
int startPriority,
int stopPriority,
LWException **exc
)
{
BOOLEAN bFileExists = FALSE;
DistroInfo distro;
PSTR symlinkTarget = NULL;
PSTR symlinkName = NULL;
long status = 0;
PSTR statusBuffer = NULL;
memset(&distro, 0, sizeof(distro));
DJ_LOG_VERBOSE("Looking for '%s'", pszChkConfigPath);
LW_CLEANUP_CTERR(exc, CTCheckFileExists(pszChkConfigPath, &bFileExists));
if (bFileExists) {
CENTERROR ceError;
DJ_LOG_VERBOSE("Found '%s'", pszChkConfigPath);
ceError = DJDoChkConfig(pszDaemonName, bStatus);
if(ceError == CENTERROR_DOMAINJOIN_CHKCONFIG_FAILED)
{
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_CHKCONFIG_FAILED, "chkconfig failed", "An error occurred while using chkconfig to process the '%s' daemon. This daemon was being %s the list of processes to start on reboot.", pszDaemonName, bStatus? "added to" : "removed from");
goto cleanup;
}
LW_CLEANUP_CTERR(exc, ceError);
goto done;
}
DJ_LOG_VERBOSE("Looking for '%s'", pszUpdateRcDFilePath);
LW_CLEANUP_CTERR(exc, CTCheckFileExists(pszUpdateRcDFilePath, &bFileExists));
if (bFileExists) {
DJ_LOG_VERBOSE("Found '%s'", pszUpdateRcDFilePath);
LW_TRY(exc, DJDoUpdateRcD(pszDaemonName, bStatus, startPriority, stopPriority, &LW_EXC));
goto done;
}
LW_CLEANUP_CTERR(exc, DJGetDistroInfo("", &distro));
if(distro.os == OS_AIX)
{
/* Programs on AIX may store their init scripts in /etc/rc.d/init.d .
* The symlinks for specific runlevels are stored in /etc/rc.d/rc2.d,
* /etc/rc.d/rc3.d, upto /etc/rc9.d . Only runlevels 0-2 are used.
* Init scripts can only be started in 0 or 1 by editing inittab. Run
* levels 3 and higher are left for the user to define.
*
* During startup, the /etc/rc.d/rc script runs all of the kill
* scripts in /etc/rc.d/rc2.d followed by all of the start scripts in
* that directory. Since the system goes to runlevel 2 at start up,
* only scripts in that directory are run.
*
* During shutdown, the /sbin/shutdown program directly (not through
* init or rc) runs all of the kill scripts in /etc/rc.d/rc*.d .
*
* So in order for a daemon to correctly be started once when the
* machine boots, and only be shutdown once when the machine shuts
* down, it should only create start and kill symlink in
* /etc/rc.d/rc2.d.
*/
if(bStatus)
{
LW_TRY(exc, FindDaemonScript(pszDaemonName,
&symlinkTarget, &LW_EXC));
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rc.d/rc2.d/K%02d%s", stopPriority, pszDaemonName));
LW_TRY(exc, DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
CT_SAFE_FREE_STRING(symlinkName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rc.d/rc2.d/S%02d%s", startPriority, pszDaemonName));
LW_TRY(exc, DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
}
else
{
PCSTR directories[] = {
"/etc/rc.d/rc2.d",
"/etc/rc.d/rc3.d",
"/etc/rc.d/rc4.d",
"/etc/rc.d/rc5.d",
"/etc/rc.d/rc6.d",
"/etc/rc.d/rc7.d",
"/etc/rc.d/rc8.d",
"/etc/rc.d/rc9.d",
NULL };
LW_TRY(exc, DJRemoveDaemonLinksInDirectories(
directories, pszDaemonName, &LW_EXC));
}
}
else if(distro.os == OS_HPUX)
{
/* On HP-UX, in order for a daemon to start in multi-user mode,
* a start symlink must be put in /sbin/rc2.d, and a stop symlink
* must be put in /sbin/rc1.d
*/
if(bStatus)
{
PCSTR relativeName = strrchr(pszDaemonName, '/');
if (relativeName == NULL)
{
relativeName = pszDaemonName;
}
else
{
relativeName++;
}
LW_TRY(exc, FindDaemonScript(pszDaemonName,
&symlinkTarget, &LW_EXC));
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/sbin/rc1.d/K%02d%s", stopPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
CT_SAFE_FREE_STRING(symlinkName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/sbin/rc2.d/S%02d%s", startPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
}
else
{
/* Remove any symlinks to the daemon if they exist */
PCSTR directories[] = {
"/sbin/rc0.d",
"/sbin/rc1.d",
"/sbin/rc2.d",
"/sbin/rc3.d",
"/sbin/rc4.d",
NULL };
LW_TRY(exc, DJRemoveDaemonLinksInDirectories(
directories, pszDaemonName, &LW_EXC));
}
}
else if(distro.os == OS_SUNOS)
{
BOOLEAN bUsesSVCS = FALSE;
LW_CLEANUP_CTERR(exc, CTCheckFileExists("/usr/sbin/svcadm", &bUsesSVCS));
if (bUsesSVCS)
{
/* On newer Solaris system, in order for a daemon to start in multi-user mode,
* we need to register our daemons with the service manager (svcs).
*
* To add:
* svccfg import /etc/likewise/svcs-solaris/.xml
* svcadm enable
*
* To remove:
* svcadm disable
* svccfg delete
*/
if(bStatus)
{
LW_CLEANUP_CTERR(exc, CTShell("/usr/sbin/svccfg import /etc/likewise/svcs-solaris/%daemonName.xml >/dev/null 2>&1; echo $? >%statusBuffer",
CTSHELL_STRING(daemonName, pszDaemonName),
CTSHELL_BUFFER(statusBuffer, &statusBuffer)));
status = atol(statusBuffer);
CT_SAFE_FREE_STRING(statusBuffer);
statusBuffer = NULL;
DJ_LOG_INFO("Daemon [%s]: svccfg import /etc/likewise/svcs-solaris/%s.xml status [%d]", pszDaemonName, pszDaemonName, status);
if (status) {
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_INCORRECT_STATUS, "svccfg import failed", "An error occurred while using svccfg to process the '%s' daemon. This daemon was being added to the list of processes to start on reboot.", pszDaemonName);
goto cleanup;
}
LW_CLEANUP_CTERR(exc, CTShell("/usr/sbin/svcadm enable %daemonName >/dev/null 2>&1; echo $? >%statusBuffer",
CTSHELL_STRING(daemonName, pszDaemonName),
CTSHELL_BUFFER(statusBuffer, &statusBuffer)));
status = atol(statusBuffer);
DJ_LOG_INFO("Daemon [%s]: svcadm enable %s status [%d]", pszDaemonName, pszDaemonName, status);
if (status) {
LW_RAISE_EX(exc, CENTERROR_DOMAINJOIN_INCORRECT_STATUS, "svcadm enable failed", "An error occurred while using svcadm to process the '%s' daemon. This daemon was being added to the list of processes to start on reboot.", pszDaemonName);
goto cleanup;
}
}
else
{
LW_CLEANUP_CTERR(exc, CTShell("/usr/sbin/svcadm disable %daemonName >/dev/null 2>&1; echo $? >%statusBuffer",
CTSHELL_STRING(daemonName, pszDaemonName),
CTSHELL_BUFFER(statusBuffer, &statusBuffer)));
status = atol(statusBuffer);
CT_SAFE_FREE_STRING(statusBuffer);
statusBuffer = NULL;
DJ_LOG_INFO("Daemon [%s]: svcadm disable %s status [%d]", pszDaemonName, pszDaemonName, status);
LW_CLEANUP_CTERR(exc, CTShell("/usr/sbin/svccfg delete %daemonName >/dev/null 2>&1; echo $? >%statusBuffer",
CTSHELL_STRING(daemonName, pszDaemonName),
CTSHELL_BUFFER(statusBuffer, &statusBuffer)));
status = atol(statusBuffer);
DJ_LOG_INFO("Daemon [%s]: svccfg delete %s status [%d]", pszDaemonName, pszDaemonName, status);
}
}
else
{
/* On older Solaris system, in order for a daemon to start in multi-user mode,
* a start symlink must be put in /etc/rc2.d, and a stop symlink
* must be put in /etc/rc0.d, /etc/rc1.d, /etc/rcS.d.
*/
if(bStatus)
{
PCSTR relativeName = strrchr(pszDaemonName, '/');
if (relativeName == NULL)
{
relativeName = pszDaemonName;
}
else
{
relativeName++;
}
LW_TRY(exc, FindDaemonScript(pszDaemonName,
&symlinkTarget, &LW_EXC));
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rc0.d/K%02d%s", stopPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
CT_SAFE_FREE_STRING(symlinkName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rc1.d/K%02d%s", stopPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
CT_SAFE_FREE_STRING(symlinkName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rcS.d/K%02d%s", stopPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
CT_SAFE_FREE_STRING(symlinkName);
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(&symlinkName,
"/etc/rc2.d/S%02d%s", startPriority, relativeName));
LW_TRY(exc,
DJOverwriteSymlink(symlinkTarget, symlinkName, &LW_EXC));
}
else
{
/* Remove any symlinks to the daemon if they exist */
PCSTR directories[] = {
"/etc/rc0.d",
"/etc/rc1.d",
"/etc/rc2.d",
"/etc/rc3.d",
"/etc/rcS.d",
NULL };
LW_TRY(exc, DJRemoveDaemonLinksInDirectories(
directories, pszDaemonName, &LW_EXC));
}
}
}
done:
cleanup:
CT_SAFE_FREE_STRING(symlinkTarget);
CT_SAFE_FREE_STRING(symlinkName);
CT_SAFE_FREE_STRING(statusBuffer);
}
void
DJManageDaemonDescription(
PCSTR pszName,
BOOLEAN bStatus,
int startPriority,
int stopPriority,
PSTR *description,
LWException **exc
)
{
BOOLEAN bStarted = FALSE;
PSTR daemonPath = NULL;
*description = NULL;
LW_TRY(exc, DJGetDaemonStatus(pszName, &bStarted, &LW_EXC));
// if we got this far, we have validated the existence of the
// daemon and we have figured out if its started or stopped
// if we are already in the desired state, do nothing.
if (bStarted != bStatus) {
CT_SAFE_FREE_STRING(daemonPath);
LW_TRY(exc, FindDaemonScript(pszName, &daemonPath, &LW_EXC));
if(bStatus)
{
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(description,
"Start %s by running '%s start'.\n"
"Create symlinks for %s so that it starts at reboot.\n",
pszName, daemonPath, pszName));
}
else
{
LW_CLEANUP_CTERR(exc, CTAllocateStringPrintf(description,
"Stop %s by running '%s stop'.\n"
"Remove symlinks for %s so that it no longer starts at reboot.\n",
pszName, daemonPath, pszName));
}
}
cleanup:
CT_SAFE_FREE_STRING(daemonPath);
}
void
DJManageDaemon(
PCSTR pszName,
BOOLEAN bStatus,
int startPriority,
int stopPriority,
LWException **exc
)
{
BOOLEAN bStarted = FALSE;
// Check for the existence of the daemon prior to doing anything.
// notice that we are using the private version so that if we fail,
// our inner exception will be the one that was tossed due to the failure.
LW_TRY(exc, DJGetDaemonStatus(pszName, &bStarted, &LW_EXC));
// Verify that daemon is configured as needed for running after restart,
// this also sets up any service control manager settings. On newer
// Solaris systems, the service control manager will start/stop the
// daemon as a result of the service configuration. It is okay to have
// this function called more than once for a given daemon, if it is already
// running or stopped it will remain the same with the same service pid.
LW_TRY(exc, DJConfigureForDaemonRestart(pszName, bStatus, startPriority, stopPriority, &LW_EXC));
// Reverify the daemon status now, it may be started or stopped as needed by the step above.
LW_TRY(exc, DJGetDaemonStatus(pszName, &bStarted, &LW_EXC));
// If we are already in the desired state, do nothing.
if (bStarted != bStatus) {
LW_TRY(exc, DJStartStopDaemon(pszName, bStatus, &LW_EXC));
}
cleanup:
;
}
struct _DaemonList daemonList[] = {
//{ "reapsysld", {NULL}, FALSE, 12, 9 },
{ "gpagentd", {NULL}, FALSE, 22, 9 },
//{ "eventfwdd", {NULL}, FALSE, 21, 9 },
{ NULL, {NULL}, FALSE, 0, 0 },
};