/*
* 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
*/
/*
*/
/*
* SMBFS I/O Daemon (SMF service)
*/
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <synch.h>
#include <time.h>
#include <unistd.h>
#include <ucred.h>
#include <wait.h>
#include <priv_utils.h>
#include <err.h>
#include <door.h>
#include <libscf.h>
#include <locale.h>
#include <thread.h>
#include <assert.h>
/* Keep a list of child processes. */
typedef struct _child {
} child_t;
static void svc_sigchld(void);
static void svc_cleanup(void);
static child_t *
{
return (cp);
}
return (NULL);
}
static child_t *
{
return (cp);
}
return (NULL);
}
/*
* Find out if the service is already running.
* Return: true, false.
*/
static boolean_t
already_running(void)
{
return (B_FALSE);
if (rc < 0)
return (B_FALSE);
return (B_TRUE);
}
/*
* This function will fork off a child process,
* from which only the child will return.
*
* The parent exit status is taken as the SMF start method
* success or failure, so the parent waits (via pipe read)
* for the child to finish initialization before it exits.
* Use SMF error codes only on exit.
*/
static int
daemonize_init(void)
{
chdir("/");
perror("pipe");
}
perror("fork");
}
/*
* If we're the parent process, wait for either the child to send us
* the appropriate exit status over the pipe or for the read to fail
* (presumably with 0 for EOF if our child terminated abnormally).
* If the read fails, exit with either the child's exit status if it
* exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
*/
if (pid != 0) {
/* parent */
}
/* child */
return (pfds[1]);
}
static void
{
/* Tell parent we're ready. */
}
int
{
int c, sig;
/* set locale and text domain for i18n */
(void) textdomain(TEXT_DOMAIN);
switch (c) {
case 'd':
/* Do debug messages. */
break;
default:
return (SMF_EXIT_ERR_CONFIG);
}
}
if (already_running()) {
return (rc);
}
/*
* Raise the fd limit to max
* errors here are non-fatal
*/
"RLIMIT_NOFILE %d, err %d",
}
/*
* Want all signals blocked, as we're doing
* synchronous delivery via sigwait below.
*/
/*
* Do want SIGCHLD, and will waitpid().
*/
/*
* Daemonize, unless debugging.
*/
if (d_flag) {
/* debug: run in foregound (not a service) */
putenv("SMBFS_DEBUG=1");
} else {
/* Non-debug: start daemon in the background. */
pfd = daemonize_init();
}
/*
* Create directory for all smbiod doors.
*/
goto out;
}
/*
* Create a file for the main service door.
*/
if (tmp_fd < 0) {
goto out;
}
tmp_fd = -1;
/* Setup the door service. */
if (door_fd == -1) {
perror("svc door_create");
goto out;
}
goto out;
}
/*
* Initializations done. Tell start method we're up.
*/
rc = SMF_EXIT_OK;
if (pfd != -1) {
pfd = -1;
}
/*
* Main thread just waits for signals.
*/
if (d_flag)
switch (sig) {
case SIGINT:
case SIGTERM:
/*
* The whole process contract gets a SIGTERM
* at once. Give children a chance to exit
* so we can do normal SIGCHLD cleanup.
* Prevent new door_open calls.
*/
alarm(2);
goto again;
case SIGALRM:
break; /* normal termination */
case SIGCHLD:
svc_sigchld();
goto again;
case SIGCONT:
goto again;
default:
/* Unexpected signal. */
break;
}
out:
if (attached)
if (door_fd != -1)
if (created)
/* NB: door threads gone now. */
svc_cleanup();
/* If startup error, report to parent. */
if (pfd != -1)
return (rc);
}
/*ARGSUSED*/
void
{
/*
* Allow a NULL arg call to check if this
* daemon is running. Just return zero.
*/
rc = 0;
goto out;
}
/*
* Get the caller's credentials.
* (from client side of door)
*/
if (door_ucred(&ucred) != 0) {
goto out;
}
/*
* Arg is just an int command code.
* Reply is also an int.
*/
goto out;
}
switch (cmd) {
case SMBIOD_START:
break;
default:
goto out;
}
out:
}
/*
* Start a per-user smbiod, if not already running.
*/
int
{
/* This UID already has an IOD. */
if (d_flag) {
" already has an iod\n", uid);
}
return (0);
}
/*
* OK, create a new child.
*/
return (ENOMEM);
}
/*
* The child will not have permission to create or
* destroy files in SMBIOD_RUNDIR so do that here.
*/
if (fd < 0) {
goto errout;
}
goto errout;
}
fd = -1;
perror("fork");
goto errout;
}
if (pid == 0) {
_exit(1);
}
/* parent */
if (d_flag) {
}
return (0);
if (fd != -1)
return (errno);
}
/*
* Assume the passed credentials (from the door client),
* drop any extra privileges, and exec the per-user iod.
*/
static int
{
if (rc != 0)
return (errno);
argv[0] = "smbiod";
return (errno);
}
static void
svc_sigchld(void)
{
found++;
if (d_flag)
continue;
}
}
/* ECHILD is the normal end of loop. */
if (found == 0)
}
static void
{
int x;
if (d_flag)
x = WEXITSTATUS(status);
if (x != 0) {
"uid %d, pid %d exit %d",
}
}
if (WIFSIGNALED(status)) {
"uid %d, pid %d signal %d",
}
}
/*
* Final cleanup before exit. Unlink child doors, etc.
* Called while single threaded, so no locks needed here.
* The list is normally empty by now due to svc_sigchld
* calls during shutdown. But in case there were any
* straglers, do cleanup here. Don't bother freeing any
* list elements here, as we're exiting.
*/
static void
svc_cleanup(void)
{
}
}