lsvcrun.c revision d28831b861181e5df28cac8efd6a92489e6feded
/*
* 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
*/
/*
*/
/*
* lsvcrun - run an rc?.d script, modifying appropriate data in the
* repository to reflect legacy behavior.
*
* We try to keep track of what we can for the legacy scripts via
* property groups under the smf/legacy_run service. Each property
* group identifies a service, named in the form 'rc2_d_S10foo'.
*
* Each group has the following properties: name, the script name
* displayed by svcs(1m); state_timestamp; contract, contract ID;
* inode, the inode of the script; and suffix, the suffix of the
* script name, e.g. 'foo'.
*
* In order to support rc scripts which delete themselves upon invocation we
* collect inode before running the script.
*
* When we run a K script, we try to identify and remove the
* property group by means of examining the inode and script
* suffix. The inode check means more than one script with the
* same suffix will still work as intended in the common case.
*
* If we cannot find a property group, or one already exists
* when we try to add one, then we print a suitable warning. These
* are warnings because there was no strict requirement that K
* and S scripts be matched up.
*
* In the face of these assumptions being proved wrong, we always
* make sure to execute the script anyway in an attempt to keep
* things working as they used to. If we can't execute the script,
* we try to leave the repository in the state it was before.
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libuutil.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/* Environment variables to pass on. See clean_environment(). */
"LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "PATH", "TZ"
};
#define EVARS_TO_PASS_NUM \
(sizeof (evars_to_pass) / sizeof (*evars_to_pass))
static void
usage()
{
}
/*
* Pick out the script name and convert it for use as an SMF property
* group name.
*/
static char *
start_pg_name(const char *path)
{
return (NULL);
}
return (NULL);
}
/* Convert illegal characters to _. */
/* locale problem? */
*cp = '_';
}
return (out);
}
static char *
script_suffix(const char *path)
{
const char *cp;
char *out;
return (NULL);
}
cp++;
if (*cp == '\0') {
return (NULL);
}
return (out);
}
/*
* Convert a path to an acceptable SMF (service) name.
*/
static char *
path_to_svc_name(const char *path)
{
return (NULL);
}
/* Convert illegal characters to _. */
/* locale problem? */
*cp = '_';
}
/* If the first character is _, use a instead. */
if (*out == '_')
*out = 'a';
return (out);
}
static void
{
}
static scf_propertygroup_t *
{
return (NULL);
scferr("scf_pg_create()");
goto out;
}
add:
SCF_PG_FLAG_NONPERSISTENT, pg) == 0) {
*ok = 1;
return (pg);
}
switch (scf_error()) {
assert(0);
abort();
/* NOTREACHED */
case SCF_ERROR_EXISTS:
break;
"Insufficient privilege to add repository properties; "
"not launching \"%s\".\n"), script);
/* NOTREACHED */
default:
scferr("scf_service_add_pg()");
goto out;
}
switch (scf_error()) {
assert(0);
abort();
/* NOTREACHED */
case SCF_ERROR_NOT_FOUND:
goto add;
default:
scferr("scf_service_get_pg()");
goto out;
}
}
scferr("scf_property_create()");
goto out;
}
/*
* See if the pg has the name property. If it has, that
* implies we successfully ran the same script before. We
* should re-run it anyway, but not modify the existing pg;
* this might lose contract-control but there's not much we
* can do.
*
* If there's no name property, then we probably couldn't
* remove the pg fully after a script failed to run.
*/
"seems to be running.\n"), script);
} else if (scf_error() != SCF_ERROR_NOT_FOUND) {
scferr("scf_pg_get_property()");
} else {
"group.\n"), script);
}
out:
return (pg);
}
static scf_propertygroup_t *
{
scferr("scf_pg_create()");
goto err;
}
scferr("scf_iter_create()");
goto err;
}
scferr("scf_property_create()");
goto err;
}
scferr("scf_value_create()");
goto err;
}
0) {
scferr("scf_iter_service_pgs_typed()");
goto err;
}
int match = 1;
prop) != 0)
continue;
continue;
if (len < 0) {
scferr("scf_value_get_astring()");
goto err;
}
continue;
}
if (ino != 0) {
prop) != 0)
continue;
continue;
continue;
}
if (match)
goto out;
}
err:
out:
return (pg);
}
/*
* Try and find the property group matching the service this script
* stops. First we look for a matching inode plus a matching suffix.
* This commonly succeeds, but if not, we just search for inode.
* Finally, we try for just the script suffix.
*/
static scf_propertygroup_t *
{
char *suffix;
return (NULL);
}
goto out;
return (NULL);
}
goto out;
goto out;
"doesn't seem to be running.\n"), script);
return (NULL);
}
out:
*ok = 1;
return (pg);
}
static scf_propertygroup_t *
{
scf_handle_t *h = NULL;
*ok = 0;
h = scf_handle_create(SCF_VERSION);
if (h == NULL) {
scferr("scf_handle_create()");
goto out;
}
if (scf_handle_bind(h) != 0) {
if (scf_error() != SCF_ERROR_NO_SERVER) {
scferr("scf_handle_bind()");
} else {
"Could not connect to svc.configd.\n"));
}
goto out;
}
scferr("scf_scope_create()");
goto out;
}
scferr("scf_service_create()");
goto out;
}
scferr("scf_handle_get_local_scope()");
goto out;
}
if (scf_error() != SCF_ERROR_NOT_FOUND) {
scferr("scf_scope_get_service()");
goto out;
}
0) {
scferr("scf_scope_add_service()");
goto out;
}
}
if (start_flag)
else
out:
return (pg);
}
static int
{
int fd;
char *svc_name;
char *svc_strbuf;
int err = 0;
do {
if (fd < 0) {
return (-1);
}
if (svc_strbuf == NULL) {
err = -1;
goto cleanup;
}
}
err = -1;
goto cleanup;
}
err = -1;
goto cleanup;
}
/* Leave HWERR in fatal set. */
if (errno != 0) {
err = -1;
goto cleanup;
}
if (svc_strbuf != NULL)
return (err);
}
static void
{
char buf[80];
if (scf_pg_delete(pg) == 0)
return;
scf_strerror(err));
}
/*
* Create a duplicate environment which only contains approved
* variables---those in evars_to_pass and those beginning with "_INIT_".
*/
static char **
approved_env(char **env)
{
char **newenv;
int i, i_new, j;
;
return (NULL);
i_new = 0;
continue;
}
for (j = 0; j < EVARS_TO_PASS_NUM; ++j) {
if (env[i][l] == '=' &&
}
}
return (newenv);
}
/*
* Create a duplicate environment which does not contain any SMF_ variables.
*/
static char **
env_without_smf(char **env)
{
char **newenv;
int i, i_new;
;
return (NULL);
i_new = 0;
continue;
}
return (newenv);
}
static int
{
scf_value_t *v;
const char *func;
const struct timeval *t;
int r;
if ((e = scf_entry_create(h)) == NULL) {
func = "scf_entry_create()";
goto err;
}
if ((v = scf_value_create(h)) == NULL) {
func = "scf_value_create()";
goto err;
}
if (r != 0) {
func = "scf_transaction_property_new()";
goto err;
}
switch (ty) {
case SCF_TYPE_COUNT:
break;
case SCF_TYPE_TIME:
t = val;
assert(r == 0);
break;
case SCF_TYPE_ASTRING:
r = scf_value_set_astring(v, val);
assert(r == 0);
break;
default:
assert(0);
abort();
}
if (scf_entry_add_value(e, v) == 0)
return (0);
func = "scf_entry_add_value()";
err:
return (-1);
}
static void
{
scf_handle_t *h;
const char *func;
char *suffix;
int ret;
h = scf_pg_handle(pg);
if (h == NULL) {
func = "scf_pg_handle()";
goto scferr;
}
goto err;
}
tx = scf_transaction_create(h);
func = "scf_transaction_create()";
goto scferr;
}
func = "scf_transaction_start()";
goto scferr;
}
/*
* We'd like to use the prettier svc_name, but if path_to_svc_name()
* fails, we can use the script name anyway.
*/
goto err;
SCF_TYPE_TIME, &tstamp) != 0)
goto err;
SCF_TYPE_COUNT, (void *)inode) != 0)
goto err;
SCF_TYPE_ASTRING, (void *)suffix) != 0)
goto err;
}
(void *)ctid) != 0)
goto err;
for (;;) {
switch (scf_transaction_commit(tx)) {
case 1:
return;
case 0:
func = "scf_pg_update()";
goto scferr;
}
continue;
case -1:
func = "scf_transaction_commit()";
goto scferr;
default:
assert(0);
abort();
}
}
err:
}
int
{
int o;
char **newenv;
int pipefds[2];
char c;
int exitstatus;
(void) uu_setpname(argv[0]);
/* Make sure we were run by svc.startd. */
switch (o) {
case 's':
source = 1;
break;
default:
usage();
}
}
usage();
start_flag = 1;
start_flag = 0;
else
usage();
/*
* Look for the pg & exit if appropriate. Also, if we're starting,
* add the pg now so we can exit before launching the script if we
* have insufficient repository privilege.
*
* If any other problem occurs, we carry on anyway.
*/
/* Clean the environment. Now so we can fail early. */
if (!source)
else
"Could not create new environment: out of memory.\n"));
cleanup_pg(pg);
}
/* pipe to communicate exec success or failure */
cleanup_pg(pg);
}
if (!pg_ok)
"despite previous errors.\n"), script);
else
script);
}
if (pid < 0) {
cleanup_pg(pg);
}
if (pid == 0) {
/* child */
if (!source) {
} else {
arg2 = "-c";
}
/* Notify parent of the failure. */
switch (errno) {
case EAGAIN:
(void) sleep(1);
/* FALLTHROUGH */
case EINTR:
continue;
}
break;
}
}
if (!start_flag)
else {
cleanup_pg(pg);
}
}
}
if (WIFSIGNALED(exitstatus)) {
char buf[SIG2STR_MAX];
} else {
}
if (start_flag)
else
cleanup_pg(pg);
}
return (UU_EXIT_OK);
}