4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 * lsvcrun - run an rc?.d script, modifying appropriate data in the
27 * repository to reflect legacy behavior.
29 * We try to keep track of what we can for the legacy scripts via
30 * property groups under the smf/legacy_run service. Each property
31 * group identifies a service, named in the form 'rc2_d_S10foo'.
33 * Each group has the following properties: name, the script name
34 * displayed by svcs(1m); state_timestamp; contract, contract ID;
35 * inode, the inode of the script; and suffix, the suffix of the
36 * script name, e.g. 'foo'.
38 * In order to support rc scripts which delete themselves upon invocation we
39 * collect inode before running the script.
41 * When we run a K script, we try to identify and remove the
42 * property group by means of examining the inode and script
43 * suffix. The inode check means more than one script with the
44 * same suffix will still work as intended in the common case.
46 * If we cannot find a property group, or one already exists
47 * when we try to add one, then we print a suitable warning. These
48 * are warnings because there was no strict requirement that K
49 * and S scripts be matched up.
51 * In the face of these assumptions being proved wrong, we always
52 * make sure to execute the script anyway in an attempt to keep
53 * things working as they used to. If we can't execute the script,
54 * we try to leave the repository in the state it was before.
58 #include <sys/types.h>
66 #include <libcontract.h>
67 #include <libcontract_priv.h>
70 #include <libscf_priv.h>
82 /* Environment variables to pass on. See clean_environment(). */
83 static char *evars_to_pass
[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
84 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "PATH", "TZ"
87 #define EVARS_TO_PASS_NUM \
88 (sizeof (evars_to_pass) / sizeof (*evars_to_pass))
94 (void) fprintf(stderr
,
95 gettext("Usage: %s [-s] script {start | stop}\n"), uu_getpname());
100 * Pick out the script name and convert it for use as an SMF property
104 start_pg_name(const char *path
)
108 if (fnmatch("/etc/rc[0-6S].d/S*", path
, FNM_PATHNAME
) != 0) {
109 uu_warn(gettext("couldn't parse name %s.\n"), path
);
113 out
= strdup(path
+ sizeof ("/etc/") - 1);
116 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno
));
120 /* Convert illegal characters to _. */
121 for (cp
= out
; *cp
!= '\0'; ++cp
) {
122 /* locale problem? */
123 if (!isalnum(*cp
) && *cp
!= '-')
131 script_suffix(const char *path
)
136 if (fnmatch("/etc/rc[0-6S].d/[SK]*", path
, FNM_PATHNAME
) != 0) {
137 uu_warn(gettext("couldn't parse name %s.\n"), path
);
141 cp
= path
+ sizeof ("/etc/rc0.d/S") - 1;
147 uu_warn(gettext("couldn't parse name %s.\n"), path
);
153 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno
));
159 * Convert a path to an acceptable SMF (service) name.
162 path_to_svc_name(const char *path
)
168 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno
));
172 /* Convert illegal characters to _. */
173 for (cp
= out
; *cp
!= '\0'; ++cp
) {
174 /* locale problem? */
175 if (!isalnum(*cp
) && *cp
!= '-' && *cp
!= '/')
179 /* If the first character is _, use a instead. */
187 scferr(const char *func
)
189 uu_warn(gettext("%s failed (%s). Repository will not be modified.\n"),
190 func
, scf_strerror(scf_error()));
193 static scf_propertygroup_t
*
194 get_start_pg(const char *script
, scf_handle_t
*h
, scf_service_t
*svc
,
197 char *pg_name
= NULL
;
198 scf_propertygroup_t
*pg
= NULL
;
199 scf_property_t
*prop
= NULL
;
201 if ((pg_name
= start_pg_name(script
)) == NULL
)
204 if ((pg
= scf_pg_create(h
)) == NULL
) {
205 scferr("scf_pg_create()");
210 if (scf_service_add_pg(svc
, pg_name
, SCF_GROUP_FRAMEWORK
,
211 SCF_PG_FLAG_NONPERSISTENT
, pg
) == 0) {
217 switch (scf_error()) {
218 case SCF_ERROR_INVALID_ARGUMENT
:
223 case SCF_ERROR_EXISTS
:
226 case SCF_ERROR_PERMISSION_DENIED
:
228 "Insufficient privilege to add repository properties; "
229 "not launching \"%s\".\n"), script
);
233 scferr("scf_service_add_pg()");
239 if (scf_service_get_pg(svc
, pg_name
, pg
) != 0) {
240 switch (scf_error()) {
241 case SCF_ERROR_INVALID_ARGUMENT
:
246 case SCF_ERROR_NOT_FOUND
:
250 scferr("scf_service_get_pg()");
257 if ((prop
= scf_property_create(h
)) == NULL
) {
258 scferr("scf_property_create()");
265 * See if the pg has the name property. If it has, that
266 * implies we successfully ran the same script before. We
267 * should re-run it anyway, but not modify the existing pg;
268 * this might lose contract-control but there's not much we
271 * If there's no name property, then we probably couldn't
272 * remove the pg fully after a script failed to run.
275 if (scf_pg_get_property(pg
, SCF_LEGACY_PROPERTY_NAME
, prop
) == 0) {
276 uu_warn(gettext("Service matching \"%s\" "
277 "seems to be running.\n"), script
);
280 } else if (scf_error() != SCF_ERROR_NOT_FOUND
) {
281 scferr("scf_pg_get_property()");
285 uu_warn(gettext("Service \"%s\" has an invalid property "
286 "group.\n"), script
);
291 scf_property_destroy(prop
);
295 static scf_propertygroup_t
*
296 pg_match(scf_handle_t
*h
, scf_service_t
*svc
, ino_t ino
, const char *suffix
)
299 scf_iter_t
*iter
= NULL
;
300 scf_propertygroup_t
*pg
= NULL
;
301 scf_property_t
*prop
= NULL
;
302 scf_value_t
*val
= NULL
;
304 if ((pg
= scf_pg_create(h
)) == NULL
) {
305 scferr("scf_pg_create()");
309 if ((iter
= scf_iter_create(h
)) == NULL
) {
310 scferr("scf_iter_create()");
314 if ((prop
= scf_property_create(h
)) == NULL
) {
315 scferr("scf_property_create()");
319 if ((val
= scf_value_create(h
)) == NULL
) {
320 scferr("scf_value_create()");
324 if (scf_iter_service_pgs_typed(iter
, svc
, SCF_GROUP_FRAMEWORK
) !=
326 scferr("scf_iter_service_pgs_typed()");
330 while (scf_iter_next_pg(iter
, pg
) > 0) {
333 if (suffix
!= NULL
) {
336 if (scf_pg_get_property(pg
, SCF_LEGACY_PROPERTY_SUFFIX
,
340 if (scf_property_get_value(prop
, val
) != 0)
343 len
= scf_value_get_astring(val
, buf
, sizeof (buf
));
345 scferr("scf_value_get_astring()");
348 if (len
>= sizeof (buf
))
351 match
= (strcmp(buf
, suffix
) == 0);
357 if (scf_pg_get_property(pg
, SCF_LEGACY_PROPERTY_INODE
,
361 if (scf_property_get_value(prop
, val
) != 0)
364 if (scf_value_get_count(val
, &pval
) != 0)
367 match
= (ino
== pval
) && match
;
379 scf_value_destroy(val
);
380 scf_iter_destroy(iter
);
381 scf_property_destroy(prop
);
386 * Try and find the property group matching the service this script
387 * stops. First we look for a matching inode plus a matching suffix.
388 * This commonly succeeds, but if not, we just search for inode.
389 * Finally, we try for just the script suffix.
391 static scf_propertygroup_t
*
392 get_stop_pg(const char *script
, scf_handle_t
*h
, scf_service_t
*svc
,
397 scf_propertygroup_t
*pg
;
399 if (stat(script
, &st
) != 0) {
400 uu_warn(gettext("Couldn't stat %s (%s).\n"), script
,
405 if ((suffix
= script_suffix(script
)) == NULL
) {
406 pg
= pg_match(h
, svc
, st
.st_ino
, NULL
);
412 if ((pg
= pg_match(h
, svc
, st
.st_ino
, suffix
)) != NULL
)
415 if ((pg
= pg_match(h
, svc
, st
.st_ino
, NULL
)) != NULL
)
418 if ((pg
= pg_match(h
, svc
, 0, suffix
)) == NULL
) {
419 uu_warn(gettext("Service matching \"%s\" "
420 "doesn't seem to be running.\n"), script
);
431 static scf_propertygroup_t
*
432 get_script_pg(const char *script
, boolean_t start_flag
, boolean_t
*ok
)
434 scf_handle_t
*h
= NULL
;
435 scf_scope_t
*scope
= NULL
;
436 scf_service_t
*svc
= NULL
;
437 scf_propertygroup_t
*pg
= NULL
;
441 h
= scf_handle_create(SCF_VERSION
);
443 scferr("scf_handle_create()");
447 if (scf_handle_bind(h
) != 0) {
448 if (scf_error() != SCF_ERROR_NO_SERVER
) {
449 scferr("scf_handle_bind()");
452 "Could not connect to svc.configd.\n"));
457 if ((scope
= scf_scope_create(h
)) == NULL
) {
458 scferr("scf_scope_create()");
462 if ((svc
= scf_service_create(h
)) == NULL
) {
463 scferr("scf_service_create()");
467 if (scf_handle_get_scope(h
, SCF_SCOPE_LOCAL
, scope
) != 0) {
468 scferr("scf_handle_get_local_scope()");
472 if (scf_scope_get_service(scope
, SCF_LEGACY_SERVICE
, svc
) != 0) {
473 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
474 scferr("scf_scope_get_service()");
478 if (scf_scope_add_service(scope
, SCF_LEGACY_SERVICE
, svc
) !=
480 scferr("scf_scope_add_service()");
486 pg
= get_start_pg(script
, h
, svc
, ok
);
488 pg
= get_stop_pg(script
, h
, svc
, ok
);
491 scf_service_destroy(svc
);
492 scf_scope_destroy(scope
);
497 prepare_contract(const char *script
, const char *action
)
505 fd
= open64(CTFS_ROOT
"/process/template", O_RDWR
);
506 } while (fd
< 0 && errno
== EINTR
);
508 uu_warn(gettext("Can not create contract"));
512 svc_strbuf
= malloc(CT_PARAM_MAX_SIZE
);
513 if (svc_strbuf
== NULL
) {
514 uu_warn(gettext("Can not allocate memory"));
519 (void) strlcpy(svc_strbuf
, SCF_FMRI_LEGACY_PREFIX
, CT_PARAM_MAX_SIZE
);
520 svc_name
= path_to_svc_name(script
);
521 (void) strlcat(svc_strbuf
, svc_name
? svc_name
: script
,
523 if (svc_name
!= NULL
) {
527 if ((errno
= ct_pr_tmpl_set_svc_fmri(fd
, svc_strbuf
)) != 0) {
528 uu_warn(gettext("Can not set svc_fmri"));
533 (void) strlcpy(svc_strbuf
, action
, CT_PARAM_MAX_SIZE
);
534 if ((errno
= ct_pr_tmpl_set_svc_aux(fd
, svc_strbuf
)) != 0) {
535 uu_warn(gettext("Can not set svc_aux"));
540 /* Leave HWERR in fatal set. */
542 errno
= ct_tmpl_activate(fd
);
544 assert(errno
== EPERM
);
545 uu_warn(gettext("Can not activate contract template"));
551 if (svc_strbuf
!= NULL
)
559 cleanup_pg(scf_propertygroup_t
*pg
)
564 if (scf_pg_delete(pg
) == 0)
569 if (scf_pg_to_fmri(pg
, buf
, sizeof (buf
)) != 0)
570 (void) strcpy(buf
, "?");
572 uu_warn(gettext("Could not remove property group %s: %s.\n"), buf
,
577 * Create a duplicate environment which only contains approved
578 * variables---those in evars_to_pass and those beginning with "_INIT_".
581 approved_env(char **env
)
586 for (i
= 0; env
[i
] != NULL
; ++i
)
589 newenv
= malloc(sizeof (*newenv
) * (i
+ 1));
595 for (i
= 0; env
[i
] != NULL
; ++i
) {
596 if (strncmp(env
[i
], "_INIT_", sizeof ("_INIT_") - 1) == 0) {
597 newenv
[i_new
++] = env
[i
];
601 for (j
= 0; j
< EVARS_TO_PASS_NUM
; ++j
) {
602 size_t l
= strlen(evars_to_pass
[j
]);
604 if (env
[i
][l
] == '=' &&
605 strncmp(env
[i
], evars_to_pass
[j
], l
) == 0)
606 newenv
[i_new
++] = env
[i
];
610 newenv
[i_new
] = NULL
;
616 * Create a duplicate environment which does not contain any SMF_ variables.
619 env_without_smf(char **env
)
624 for (i
= 0; env
[i
] != NULL
; ++i
)
627 newenv
= malloc(sizeof (*newenv
) * (i
+ 1));
633 for (i
= 0; env
[i
] != NULL
; ++i
) {
634 if (strncmp(env
[i
], "SMF_", sizeof ("SMF_") - 1) == 0)
637 newenv
[i_new
++] = env
[i
];
640 newenv
[i_new
] = NULL
;
646 add_new_property(scf_handle_t
*h
, scf_transaction_t
*tx
, const char *name
,
647 scf_type_t ty
, const void *val
)
649 scf_transaction_entry_t
*e
;
652 const struct timeval
*t
;
655 if ((e
= scf_entry_create(h
)) == NULL
) {
656 func
= "scf_entry_create()";
660 if ((v
= scf_value_create(h
)) == NULL
) {
661 func
= "scf_value_create()";
665 r
= scf_transaction_property_new(tx
, e
, name
, ty
);
667 func
= "scf_transaction_property_new()";
673 scf_value_set_count(v
, (uint64_t)(uintptr_t)val
);
678 r
= scf_value_set_time(v
, t
->tv_sec
, 1000 * t
->tv_usec
);
682 case SCF_TYPE_ASTRING
:
683 r
= scf_value_set_astring(v
, val
);
692 if (scf_entry_add_value(e
, v
) == 0)
695 func
= "scf_entry_add_value()";
698 uu_warn(gettext("%s failed (%s).\n"), func
, scf_strerror(scf_error()));
703 set_legacy_service(scf_propertygroup_t
*pg
, const char *script
, ino_t inode
)
708 scf_transaction_t
*tx
;
709 struct timeval tstamp
;
711 char *svc_name
= NULL
;
714 h
= scf_pg_handle(pg
);
716 func
= "scf_pg_handle()";
720 ret
= gettimeofday(&tstamp
, NULL
);
723 if (errno
= contract_latest(&ctid
)) {
724 uu_warn(gettext("Could not get contract"));
728 tx
= scf_transaction_create(h
);
730 func
= "scf_transaction_create()";
734 if (scf_transaction_start(tx
, pg
) != 0) {
735 func
= "scf_transaction_start()";
740 * We'd like to use the prettier svc_name, but if path_to_svc_name()
741 * fails, we can use the script name anyway.
743 svc_name
= path_to_svc_name(script
);
745 if (add_new_property(h
, tx
, SCF_LEGACY_PROPERTY_NAME
, SCF_TYPE_ASTRING
,
746 (void *)(svc_name
? svc_name
: script
)) != 0)
749 if (add_new_property(h
, tx
, SCF_PROPERTY_STATE_TIMESTAMP
,
750 SCF_TYPE_TIME
, &tstamp
) != 0)
753 if (add_new_property(h
, tx
, SCF_LEGACY_PROPERTY_INODE
,
754 SCF_TYPE_COUNT
, (void *)inode
) != 0)
757 if ((suffix
= script_suffix(script
)) != NULL
) {
758 if (add_new_property(h
, tx
, SCF_LEGACY_PROPERTY_SUFFIX
,
759 SCF_TYPE_ASTRING
, (void *)suffix
) != 0)
765 if (add_new_property(h
, tx
, SCF_PROPERTY_CONTRACT
, SCF_TYPE_COUNT
,
770 switch (scf_transaction_commit(tx
)) {
776 if (scf_pg_update(pg
) == -1) {
777 func
= "scf_pg_update()";
783 func
= "scf_transaction_commit()";
793 uu_warn(gettext("%s failed (%s).\n"), func
, scf_strerror(scf_error()));
795 uu_die(gettext("Could not commit property values to repository.\n"));
799 main(int argc
, char *argv
[], char *envp
[])
801 const char *restarter
, *script
, *action
;
802 boolean_t source
= 0;
804 boolean_t start_flag
;
812 scf_propertygroup_t
*pg
;
815 (void) uu_setpname(argv
[0]);
816 uu_alt_exit(UU_PROFILE_LAUNCHER
);
818 /* Make sure we were run by svc.startd. */
819 if ((restarter
= getenv("SMF_RESTARTER")) == NULL
||
820 strcmp(restarter
, SCF_SERVICE_STARTD
) != 0)
821 uu_die(gettext("invocation outside smf(5) inappropriate\n"));
823 while ((o
= getopt(argc
, argv
, "s")) != -1) {
834 if (argc
- optind
!= 2)
837 script
= argv
[optind
];
838 action
= argv
[optind
+ 1];
840 if (strcmp(action
, "start") == 0)
842 else if (strcmp(action
, "stop") == 0)
848 * Look for the pg & exit if appropriate. Also, if we're starting,
849 * add the pg now so we can exit before launching the script if we
850 * have insufficient repository privilege.
852 * If any other problem occurs, we carry on anyway.
854 pg
= get_script_pg(script
, start_flag
, &pg_ok
);
856 /* Clean the environment. Now so we can fail early. */
858 newenv
= approved_env(envp
);
860 newenv
= env_without_smf(envp
);
863 "Could not create new environment: out of memory.\n"));
865 if (prepare_contract(script
, action
) == -1) {
866 if (start_flag
&& pg
!= NULL
)
872 /* pipe to communicate exec success or failure */
873 if (pipe(pipefds
) != 0) {
874 uu_warn(gettext("Could not create pipe"));
876 if (start_flag
&& pg
!= NULL
)
883 (void) printf(gettext("Executing legacy init script \"%s\" "
884 "despite previous errors.\n"), script
);
886 (void) printf(gettext("Executing legacy init script \"%s\".\n"),
888 (void) fflush(stdout
);
890 if (stat(script
, &st
) != 0) {
891 uu_warn(gettext("Couldn't stat %s (%s).\n"), script
,
893 st
.st_ino
= (ino_t
)0;
898 uu_warn(gettext("Could not fork"));
900 if (start_flag
&& pg
!= NULL
)
909 const char *arg1
, *arg2
, *arg3
;
911 (void) close(pipefds
[0]);
912 (void) fcntl(pipefds
[1], F_SETFD
, FD_CLOEXEC
);
924 (void) execle(arg1
, arg1
, arg2
, arg3
, NULL
, newenv
);
926 uu_warn(gettext("Could not exec \"%s %s %s\""), arg1
,
930 /* Notify parent of the failure. */
931 while (write(pipefds
[1], &c
, 1) != 1) {
942 uu_warn(gettext("Could not inform parent of error"));
949 (void) close(pipefds
[1]);
951 if (read(pipefds
[0], &c
, sizeof (c
)) > 0) {
953 uu_die(gettext("exec() failed; leaving properties.\n"));
955 uu_warn(gettext("exec() failed.\n"));
962 while (waitpid(pid
, &exitstatus
, 0) == -1) {
963 assert(errno
== EINTR
);
966 if (WIFSIGNALED(exitstatus
)) {
967 char buf
[SIG2STR_MAX
];
968 (void) sig2str(WTERMSIG(exitstatus
), buf
);
969 (void) printf(gettext("Legacy init script \"%s\" failed due "
970 "to signal %s.\n"), script
, buf
);
972 (void) printf(gettext("Legacy init script \"%s\" exited with "
973 "return code %d.\n"), script
, WEXITSTATUS(exitstatus
));
978 set_legacy_service(pg
, script
, st
.st_ino
);