1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * lsvcrun - run an rc?.d script, modifying appropriate data in the
27 * repository to reflect legacy behavior.
28 *
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'.
32 *
33 * Each group has the following properties: name, the script name
34 * displayed by svcs(1); state_timestamp; contract, contract ID;
35 * inode, the inode of the script; and suffix, the suffix of the
36 * script name, e.g. 'foo'.
37 *
38 * In order to support rc scripts which delete themselves upon invocation we
39 * collect inode before running the script.
40 *
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.
45 *
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.
50 *
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.
55 */
56
57 #include <sys/ctfs.h>
58 #include <sys/types.h>
59 #include <sys/wait.h>
60 #include <sys/stat.h>
61 #include <assert.h>
62 #include <ctype.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <fnmatch.h>
66 #include <libcontract.h>
67 #include <libcontract_priv.h>
68 #include <libintl.h>
69 #include <libscf.h>
70 #include <libscf_priv.h>
71 #include <libuutil.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <strings.h>
77 #include <time.h>
78 #include <unistd.h>
79 #include <limits.h>
80
81
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"
85 };
86
87 #define EVARS_TO_PASS_NUM \
88 (sizeof (evars_to_pass) / sizeof (*evars_to_pass))
89
90
91 static void
usage()92 usage()
93 {
94 (void) fprintf(stderr,
95 gettext("Usage: %s [-s] script {start | stop}\n"), uu_getpname());
96 exit(UU_EXIT_USAGE);
97 }
98
99 /*
100 * Pick out the script name and convert it for use as an SMF property
101 * group name.
102 */
103 static char *
start_pg_name(const char * path)104 start_pg_name(const char *path)
105 {
106 char *out, *cp;
107
108 if (fnmatch("/etc/rc[0-6S].d/S*", path, FNM_PATHNAME) != 0) {
109 uu_warn(gettext("couldn't parse name %s.\n"), path);
110 return (NULL);
111 }
112
113 out = strdup(path + sizeof ("/etc/") - 1);
114
115 if (out == NULL) {
116 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno));
117 return (NULL);
118 }
119
120 /* Convert illegal characters to _. */
121 for (cp = out; *cp != '\0'; ++cp) {
122 /* locale problem? */
123 if (!isalnum(*cp) && *cp != '-')
124 *cp = '_';
125 }
126
127 return (out);
128 }
129
130 static char *
script_suffix(const char * path)131 script_suffix(const char *path)
132 {
133 const char *cp;
134 char *out;
135
136 if (fnmatch("/etc/rc[0-6S].d/[SK]*", path, FNM_PATHNAME) != 0) {
137 uu_warn(gettext("couldn't parse name %s.\n"), path);
138 return (NULL);
139 }
140
141 cp = path + sizeof ("/etc/rc0.d/S") - 1;
142
143 while (isdigit(*cp))
144 cp++;
145
146 if (*cp == '\0') {
147 uu_warn(gettext("couldn't parse name %s.\n"), path);
148 return (NULL);
149 }
150
151 out = strdup(cp);
152 if (out == NULL)
153 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno));
154
155 return (out);
156 }
157
158 /*
159 * Convert a path to an acceptable SMF (service) name.
160 */
161 static char *
path_to_svc_name(const char * path)162 path_to_svc_name(const char *path)
163 {
164 char *out, *cp;
165
166 out = strdup(path);
167 if (out == NULL) {
168 uu_warn(gettext("strdup() failed (%s).\n"), strerror(errno));
169 return (NULL);
170 }
171
172 /* Convert illegal characters to _. */
173 for (cp = out; *cp != '\0'; ++cp) {
174 /* locale problem? */
175 if (!isalnum(*cp) && *cp != '-' && *cp != '/')
176 *cp = '_';
177 }
178
179 /* If the first character is _, use a instead. */
180 if (*out == '_')
181 *out = 'a';
182
183 return (out);
184 }
185
186 static void
scferr(const char * func)187 scferr(const char *func)
188 {
189 uu_warn(gettext("%s failed (%s). Repository will not be modified.\n"),
190 func, scf_strerror(scf_error()));
191 }
192
193 static scf_propertygroup_t *
get_start_pg(const char * script,scf_handle_t * h,scf_service_t * svc,boolean_t * ok)194 get_start_pg(const char *script, scf_handle_t *h, scf_service_t *svc,
195 boolean_t *ok)
196 {
197 char *pg_name = NULL;
198 scf_propertygroup_t *pg = NULL;
199 scf_property_t *prop = NULL;
200
201 if ((pg_name = start_pg_name(script)) == NULL)
202 return (NULL);
203
204 if ((pg = scf_pg_create(h)) == NULL) {
205 scferr("scf_pg_create()");
206 goto out;
207 }
208
209 add:
210 if (scf_service_add_pg(svc, pg_name, SCF_GROUP_FRAMEWORK,
211 SCF_PG_FLAG_NONPERSISTENT, pg) == 0) {
212 *ok = 1;
213 free(pg_name);
214 return (pg);
215 }
216
217 switch (scf_error()) {
218 case SCF_ERROR_INVALID_ARGUMENT:
219 assert(0);
220 abort();
221 /* NOTREACHED */
222
223 case SCF_ERROR_EXISTS:
224 break;
225
226 case SCF_ERROR_PERMISSION_DENIED:
227 uu_die(gettext(
228 "Insufficient privilege to add repository properties; "
229 "not launching \"%s\".\n"), script);
230 /* NOTREACHED */
231
232 default:
233 scferr("scf_service_add_pg()");
234 scf_pg_destroy(pg);
235 pg = NULL;
236 goto out;
237 }
238
239 if (scf_service_get_pg(svc, pg_name, pg) != 0) {
240 switch (scf_error()) {
241 case SCF_ERROR_INVALID_ARGUMENT:
242 assert(0);
243 abort();
244 /* NOTREACHED */
245
246 case SCF_ERROR_NOT_FOUND:
247 goto add;
248
249 default:
250 scferr("scf_service_get_pg()");
251 scf_pg_destroy(pg);
252 pg = NULL;
253 goto out;
254 }
255 }
256
257 if ((prop = scf_property_create(h)) == NULL) {
258 scferr("scf_property_create()");
259 scf_pg_destroy(pg);
260 pg = NULL;
261 goto out;
262 }
263
264 /*
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
269 * can do.
270 *
271 * If there's no name property, then we probably couldn't
272 * remove the pg fully after a script failed to run.
273 */
274
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);
278 scf_pg_destroy(pg);
279 pg = NULL;
280 } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
281 scferr("scf_pg_get_property()");
282 scf_pg_destroy(pg);
283 pg = NULL;
284 } else {
285 uu_warn(gettext("Service \"%s\" has an invalid property "
286 "group.\n"), script);
287 }
288
289 out:
290 free(pg_name);
291 scf_property_destroy(prop);
292 return (pg);
293 }
294
295 static scf_propertygroup_t *
pg_match(scf_handle_t * h,scf_service_t * svc,ino_t ino,const char * suffix)296 pg_match(scf_handle_t *h, scf_service_t *svc, ino_t ino, const char *suffix)
297 {
298 char buf[PATH_MAX];
299 scf_iter_t *iter = NULL;
300 scf_propertygroup_t *pg = NULL;
301 scf_property_t *prop = NULL;
302 scf_value_t *val = NULL;
303
304 if ((pg = scf_pg_create(h)) == NULL) {
305 scferr("scf_pg_create()");
306 goto err;
307 }
308
309 if ((iter = scf_iter_create(h)) == NULL) {
310 scferr("scf_iter_create()");
311 goto err;
312 }
313
314 if ((prop = scf_property_create(h)) == NULL) {
315 scferr("scf_property_create()");
316 goto err;
317 }
318
319 if ((val = scf_value_create(h)) == NULL) {
320 scferr("scf_value_create()");
321 goto err;
322 }
323
324 if (scf_iter_service_pgs_typed(iter, svc, SCF_GROUP_FRAMEWORK) !=
325 0) {
326 scferr("scf_iter_service_pgs_typed()");
327 goto err;
328 }
329
330 while (scf_iter_next_pg(iter, pg) > 0) {
331 int match = 1;
332
333 if (suffix != NULL) {
334 ssize_t len;
335
336 if (scf_pg_get_property(pg, SCF_LEGACY_PROPERTY_SUFFIX,
337 prop) != 0)
338 continue;
339
340 if (scf_property_get_value(prop, val) != 0)
341 continue;
342
343 len = scf_value_get_astring(val, buf, sizeof (buf));
344 if (len < 0) {
345 scferr("scf_value_get_astring()");
346 goto err;
347 }
348 if (len >= sizeof (buf))
349 continue;
350
351 match = (strcmp(buf, suffix) == 0);
352 }
353
354 if (ino != 0) {
355 uint64_t pval;
356
357 if (scf_pg_get_property(pg, SCF_LEGACY_PROPERTY_INODE,
358 prop) != 0)
359 continue;
360
361 if (scf_property_get_value(prop, val) != 0)
362 continue;
363
364 if (scf_value_get_count(val, &pval) != 0)
365 continue;
366
367 match = (ino == pval) && match;
368 }
369
370 if (match)
371 goto out;
372 }
373
374 err:
375 scf_pg_destroy(pg);
376 pg = NULL;
377
378 out:
379 scf_value_destroy(val);
380 scf_iter_destroy(iter);
381 scf_property_destroy(prop);
382 return (pg);
383 }
384
385 /*
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.
390 */
391 static scf_propertygroup_t *
get_stop_pg(const char * script,scf_handle_t * h,scf_service_t * svc,boolean_t * ok)392 get_stop_pg(const char *script, scf_handle_t *h, scf_service_t *svc,
393 boolean_t *ok)
394 {
395 struct stat st;
396 char *suffix;
397 scf_propertygroup_t *pg;
398
399 if (stat(script, &st) != 0) {
400 uu_warn(gettext("Couldn't stat %s (%s).\n"), script,
401 strerror(errno));
402 return (NULL);
403 }
404
405 if ((suffix = script_suffix(script)) == NULL) {
406 pg = pg_match(h, svc, st.st_ino, NULL);
407 if (pg != NULL)
408 goto out;
409 return (NULL);
410 }
411
412 if ((pg = pg_match(h, svc, st.st_ino, suffix)) != NULL)
413 goto out;
414
415 if ((pg = pg_match(h, svc, st.st_ino, NULL)) != NULL)
416 goto out;
417
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);
421 free(suffix);
422 return (NULL);
423 }
424
425 out:
426 *ok = 1;
427 free(suffix);
428 return (pg);
429 }
430
431 static scf_propertygroup_t *
get_script_pg(const char * script,boolean_t start_flag,boolean_t * ok)432 get_script_pg(const char *script, boolean_t start_flag, boolean_t *ok)
433 {
434 scf_handle_t *h = NULL;
435 scf_scope_t *scope = NULL;
436 scf_service_t *svc = NULL;
437 scf_propertygroup_t *pg = NULL;
438
439 *ok = 0;
440
441 h = scf_handle_create(SCF_VERSION);
442 if (h == NULL) {
443 scferr("scf_handle_create()");
444 goto out;
445 }
446
447 if (scf_handle_bind(h) != 0) {
448 if (scf_error() != SCF_ERROR_NO_SERVER) {
449 scferr("scf_handle_bind()");
450 } else {
451 uu_warn(gettext(
452 "Could not connect to svc.configd.\n"));
453 }
454 goto out;
455 }
456
457 if ((scope = scf_scope_create(h)) == NULL) {
458 scferr("scf_scope_create()");
459 goto out;
460 }
461
462 if ((svc = scf_service_create(h)) == NULL) {
463 scferr("scf_service_create()");
464 goto out;
465 }
466
467 if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) {
468 scferr("scf_handle_get_local_scope()");
469 goto out;
470 }
471
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()");
475 goto out;
476 }
477
478 if (scf_scope_add_service(scope, SCF_LEGACY_SERVICE, svc) !=
479 0) {
480 scferr("scf_scope_add_service()");
481 goto out;
482 }
483 }
484
485 if (start_flag)
486 pg = get_start_pg(script, h, svc, ok);
487 else
488 pg = get_stop_pg(script, h, svc, ok);
489
490 out:
491 scf_service_destroy(svc);
492 scf_scope_destroy(scope);
493 return (pg);
494 }
495
496 static int
prepare_contract(const char * script,const char * action)497 prepare_contract(const char *script, const char *action)
498 {
499 int fd;
500 char *svc_name;
501 char *svc_strbuf;
502 int err = 0;
503
504 do {
505 fd = open64(CTFS_ROOT "/process/template", O_RDWR);
506 } while (fd < 0 && errno == EINTR);
507 if (fd < 0) {
508 uu_warn(gettext("Can not create contract"));
509 return (-1);
510 }
511
512 svc_strbuf = malloc(CT_PARAM_MAX_SIZE);
513 if (svc_strbuf == NULL) {
514 uu_warn(gettext("Can not allocate memory"));
515 err = -1;
516 goto cleanup;
517 }
518
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,
522 CT_PARAM_MAX_SIZE);
523 if (svc_name != NULL) {
524 free(svc_name);
525 }
526
527 if ((errno = ct_pr_tmpl_set_svc_fmri(fd, svc_strbuf)) != 0) {
528 uu_warn(gettext("Can not set svc_fmri"));
529 err = -1;
530 goto cleanup;
531 }
532
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"));
536 err = -1;
537 goto cleanup;
538 }
539
540 /* Leave HWERR in fatal set. */
541
542 errno = ct_tmpl_activate(fd);
543 if (errno != 0) {
544 assert(errno == EPERM);
545 uu_warn(gettext("Can not activate contract template"));
546 err = -1;
547 goto cleanup;
548 }
549
550 cleanup:
551 if (svc_strbuf != NULL)
552 free(svc_strbuf);
553 (void) close(fd);
554
555 return (err);
556 }
557
558 static void
cleanup_pg(scf_propertygroup_t * pg)559 cleanup_pg(scf_propertygroup_t *pg)
560 {
561 scf_error_t err;
562 char buf[80];
563
564 if (scf_pg_delete(pg) == 0)
565 return;
566
567 err = scf_error();
568
569 if (scf_pg_to_fmri(pg, buf, sizeof (buf)) != 0)
570 (void) strcpy(buf, "?");
571
572 uu_warn(gettext("Could not remove property group %s: %s.\n"), buf,
573 scf_strerror(err));
574 }
575
576 /*
577 * Create a duplicate environment which only contains approved
578 * variables---those in evars_to_pass and those beginning with "_INIT_".
579 */
580 static char **
approved_env(char ** env)581 approved_env(char **env)
582 {
583 char **newenv;
584 int i, i_new, j;
585
586 for (i = 0; env[i] != NULL; ++i)
587 ;
588
589 newenv = malloc(sizeof (*newenv) * (i + 1));
590 if (newenv == NULL)
591 return (NULL);
592
593 i_new = 0;
594
595 for (i = 0; env[i] != NULL; ++i) {
596 if (strncmp(env[i], "_INIT_", sizeof ("_INIT_") - 1) == 0) {
597 newenv[i_new++] = env[i];
598 continue;
599 }
600
601 for (j = 0; j < EVARS_TO_PASS_NUM; ++j) {
602 size_t l = strlen(evars_to_pass[j]);
603
604 if (env[i][l] == '=' &&
605 strncmp(env[i], evars_to_pass[j], l) == 0)
606 newenv[i_new++] = env[i];
607 }
608 }
609
610 newenv[i_new] = NULL;
611
612 return (newenv);
613 }
614
615 /*
616 * Create a duplicate environment which does not contain any SMF_ variables.
617 */
618 static char **
env_without_smf(char ** env)619 env_without_smf(char **env)
620 {
621 char **newenv;
622 int i, i_new;
623
624 for (i = 0; env[i] != NULL; ++i)
625 ;
626
627 newenv = malloc(sizeof (*newenv) * (i + 1));
628 if (newenv == NULL)
629 return (NULL);
630
631 i_new = 0;
632
633 for (i = 0; env[i] != NULL; ++i) {
634 if (strncmp(env[i], "SMF_", sizeof ("SMF_") - 1) == 0)
635 continue;
636
637 newenv[i_new++] = env[i];
638 }
639
640 newenv[i_new] = NULL;
641
642 return (newenv);
643 }
644
645 static int
add_new_property(scf_handle_t * h,scf_transaction_t * tx,const char * name,scf_type_t ty,const void * val)646 add_new_property(scf_handle_t *h, scf_transaction_t *tx, const char *name,
647 scf_type_t ty, const void *val)
648 {
649 scf_transaction_entry_t *e;
650 scf_value_t *v;
651 const char *func;
652 const struct timeval *t;
653 int r;
654
655 if ((e = scf_entry_create(h)) == NULL) {
656 func = "scf_entry_create()";
657 goto err;
658 }
659
660 if ((v = scf_value_create(h)) == NULL) {
661 func = "scf_value_create()";
662 goto err;
663 }
664
665 r = scf_transaction_property_new(tx, e, name, ty);
666 if (r != 0) {
667 func = "scf_transaction_property_new()";
668 goto err;
669 }
670
671 switch (ty) {
672 case SCF_TYPE_COUNT:
673 scf_value_set_count(v, (uint64_t)(uintptr_t)val);
674 break;
675
676 case SCF_TYPE_TIME:
677 t = val;
678 r = scf_value_set_time(v, t->tv_sec, 1000 * t->tv_usec);
679 assert(r == 0);
680 break;
681
682 case SCF_TYPE_ASTRING:
683 r = scf_value_set_astring(v, val);
684 assert(r == 0);
685 break;
686
687 default:
688 assert(0);
689 abort();
690 }
691
692 if (scf_entry_add_value(e, v) == 0)
693 return (0);
694
695 func = "scf_entry_add_value()";
696
697 err:
698 uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error()));
699 return (-1);
700 }
701
702 static void
set_legacy_service(scf_propertygroup_t * pg,const char * script,ino_t inode)703 set_legacy_service(scf_propertygroup_t *pg, const char *script, ino_t inode)
704 {
705 scf_handle_t *h;
706 const char *func;
707 char *suffix;
708 scf_transaction_t *tx;
709 struct timeval tstamp;
710 ctid_t ctid;
711 char *svc_name = NULL;
712 int ret;
713
714 h = scf_pg_handle(pg);
715 if (h == NULL) {
716 func = "scf_pg_handle()";
717 goto scferr;
718 }
719
720 ret = gettimeofday(&tstamp, NULL);
721 assert(ret == 0);
722
723 if (errno = contract_latest(&ctid)) {
724 uu_warn(gettext("Could not get contract"));
725 goto err;
726 }
727
728 tx = scf_transaction_create(h);
729 if (tx == NULL) {
730 func = "scf_transaction_create()";
731 goto scferr;
732 }
733
734 if (scf_transaction_start(tx, pg) != 0) {
735 func = "scf_transaction_start()";
736 goto scferr;
737 }
738
739 /*
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.
742 */
743 svc_name = path_to_svc_name(script);
744
745 if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_NAME, SCF_TYPE_ASTRING,
746 (void *)(svc_name ? svc_name : script)) != 0)
747 goto err;
748
749 if (add_new_property(h, tx, SCF_PROPERTY_STATE_TIMESTAMP,
750 SCF_TYPE_TIME, &tstamp) != 0)
751 goto err;
752
753 if (add_new_property(h, tx, SCF_LEGACY_PROPERTY_INODE,
754 SCF_TYPE_COUNT, (void *)inode) != 0)
755 goto err;
756
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)
760 goto err;
761
762 free(suffix);
763 }
764
765 if (add_new_property(h, tx, SCF_PROPERTY_CONTRACT, SCF_TYPE_COUNT,
766 (void *)ctid) != 0)
767 goto err;
768
769 for (;;) {
770 switch (scf_transaction_commit(tx)) {
771 case 1:
772 free(svc_name);
773 return;
774
775 case 0:
776 if (scf_pg_update(pg) == -1) {
777 func = "scf_pg_update()";
778 goto scferr;
779 }
780 continue;
781
782 case -1:
783 func = "scf_transaction_commit()";
784 goto scferr;
785
786 default:
787 assert(0);
788 abort();
789 }
790 }
791
792 scferr:
793 uu_warn(gettext("%s failed (%s).\n"), func, scf_strerror(scf_error()));
794 err:
795 uu_die(gettext("Could not commit property values to repository.\n"));
796 }
797
798 int
main(int argc,char * argv[],char * envp[])799 main(int argc, char *argv[], char *envp[])
800 {
801 const char *restarter, *script, *action;
802 boolean_t source = 0;
803 int o;
804 boolean_t start_flag;
805 char **newenv;
806 pid_t pid;
807 int pipefds[2];
808 char c;
809 int exitstatus;
810 struct stat st;
811
812 scf_propertygroup_t *pg;
813 boolean_t pg_ok;
814
815 (void) uu_setpname(argv[0]);
816 uu_alt_exit(UU_PROFILE_LAUNCHER);
817
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(7) inappropriate\n"));
822
823 while ((o = getopt(argc, argv, "s")) != -1) {
824 switch (o) {
825 case 's':
826 source = 1;
827 break;
828
829 default:
830 usage();
831 }
832 }
833
834 if (argc - optind != 2)
835 usage();
836
837 script = argv[optind];
838 action = argv[optind + 1];
839
840 if (strcmp(action, "start") == 0)
841 start_flag = 1;
842 else if (strcmp(action, "stop") == 0)
843 start_flag = 0;
844 else
845 usage();
846
847 /*
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.
851 *
852 * If any other problem occurs, we carry on anyway.
853 */
854 pg = get_script_pg(script, start_flag, &pg_ok);
855
856 /* Clean the environment. Now so we can fail early. */
857 if (!source)
858 newenv = approved_env(envp);
859 else
860 newenv = env_without_smf(envp);
861 if (newenv == NULL)
862 uu_die(gettext(
863 "Could not create new environment: out of memory.\n"));
864
865 if (prepare_contract(script, action) == -1) {
866 if (start_flag && pg != NULL)
867 cleanup_pg(pg);
868
869 exit(UU_EXIT_FATAL);
870 }
871
872 /* pipe to communicate exec success or failure */
873 if (pipe(pipefds) != 0) {
874 uu_warn(gettext("Could not create pipe"));
875
876 if (start_flag && pg != NULL)
877 cleanup_pg(pg);
878
879 exit(UU_EXIT_FATAL);
880 }
881
882 if (!pg_ok)
883 (void) printf(gettext("Executing legacy init script \"%s\" "
884 "despite previous errors.\n"), script);
885 else
886 (void) printf(gettext("Executing legacy init script \"%s\".\n"),
887 script);
888 (void) fflush(stdout);
889
890 if (stat(script, &st) != 0) {
891 uu_warn(gettext("Couldn't stat %s (%s).\n"), script,
892 strerror(errno));
893 st.st_ino = (ino_t)0;
894 }
895
896 pid = fork();
897 if (pid < 0) {
898 uu_warn(gettext("Could not fork"));
899
900 if (start_flag && pg != NULL)
901 cleanup_pg(pg);
902
903 exit(UU_EXIT_FATAL);
904 }
905
906 if (pid == 0) {
907 /* child */
908
909 const char *arg1, *arg2, *arg3;
910
911 (void) close(pipefds[0]);
912 (void) fcntl(pipefds[1], F_SETFD, FD_CLOEXEC);
913
914 if (!source) {
915 arg1 = "/bin/sh";
916 arg2 = script;
917 arg3 = action;
918 } else {
919 arg1 = "/bin/sh";
920 arg2 = "-c";
921 arg3 = script;
922 }
923
924 (void) execle(arg1, arg1, arg2, arg3, NULL, newenv);
925
926 uu_warn(gettext("Could not exec \"%s %s %s\""), arg1,
927 arg2, arg3);
928
929
930 /* Notify parent of the failure. */
931 while (write(pipefds[1], &c, 1) != 1) {
932 switch (errno) {
933 case EAGAIN:
934 (void) sleep(1);
935
936 /* FALLTHROUGH */
937
938 case EINTR:
939 continue;
940 }
941
942 uu_warn(gettext("Could not inform parent of error"));
943 break;
944 }
945
946 exit(UU_EXIT_FATAL);
947 }
948
949 (void) close(pipefds[1]);
950
951 if (read(pipefds[0], &c, sizeof (c)) > 0) {
952 if (!start_flag)
953 uu_die(gettext("exec() failed; leaving properties.\n"));
954 else {
955 uu_warn(gettext("exec() failed.\n"));
956 if (pg != NULL)
957 cleanup_pg(pg);
958 exit(UU_EXIT_FATAL);
959 }
960 }
961
962 while (waitpid(pid, &exitstatus, 0) == -1) {
963 assert(errno == EINTR);
964 }
965
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);
971 } else {
972 (void) printf(gettext("Legacy init script \"%s\" exited with "
973 "return code %d.\n"), script, WEXITSTATUS(exitstatus));
974 }
975
976 if (pg != NULL) {
977 if (start_flag)
978 set_legacy_service(pg, script, st.st_ino);
979 else
980 cleanup_pg(pg);
981 scf_pg_destroy(pg);
982 }
983
984 return (UU_EXIT_OK);
985 }
986