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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * inetconv - convert inetd.conf entries into smf(5) service manifests,
28 * import them into smf(5) repository
29 */
30
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/wait.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <pwd.h>
41 #include <grp.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <locale.h>
45 #include <libintl.h>
46 #include <libscf.h>
47 #include <inetsvc.h>
48 #include <rpc/nettype.h>
49
50 /* exit codes */
51 #define EXIT_SUCCESS 0 /* succeeded */
52 #define EXIT_USAGE 1 /* bad options */
53 #define EXIT_ERROR_CONV 2 /* error(s) coverting inetd.conf entries */
54 #define EXIT_ERROR_IMP 3 /* error(s) importing manifests */
55 #define EXIT_ERROR_SYS 4 /* system error */
56 #define EXIT_ERROR_ENBL 5 /* error(s) enabling services */
57
58 #ifndef TEXT_DOMAIN
59 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
60 #endif
61
62 #define MAIN_CONFIG "/etc/inet/inetd.conf"
63 #define ALT_CONFIG "/etc/inetd.conf"
64
65 #define MANIFEST_DIR "/lib/svc/manifest/network"
66 #define MANIFEST_RPC_DIR MANIFEST_DIR "/rpc"
67 #define SVCCFG_PATH "/usr/sbin/svccfg"
68
69 #define RPCBIND_FMRI "svc:/network/rpc/bind"
70
71 /* maximum allowed length of an inetd.conf format line */
72 #define MAX_SRC_LINELEN 32768
73
74 /* Version of inetconv, used as a marker in services we generate */
75 #define INETCONV_VERSION 1
76
77 struct inetconfent {
78 /* fields as read from inetd.conf format line */
79 char *service;
80 char *endpoint;
81 char *protocol;
82 char *wait_status;
83 char *username;
84 char *server_program;
85 char *server_args;
86 /* information derived from above fields */
87 boolean_t wait;
88 boolean_t isrpc;
89 int rpc_low_version;
90 int rpc_high_version;
91 char *rpc_prog;
92 char *groupname;
93 char *exec;
94 char *arg0;
95 };
96
97 struct fileinfo {
98 FILE *fp;
99 char *filename;
100 int lineno;
101 int failcnt;
102 };
103
104 static char *progname;
105
106 static boolean_t import = B_TRUE;
107
108 /* start of manifest XML template strings */
109 static const char xml_header[] =
110 "<?xml version='1.0'?>\n"
111 "<!DOCTYPE service_bundle SYSTEM "
112 "'/usr/share/lib/xml/dtd/service_bundle.dtd.1'>\n";
113
114 static const char xml_comment[] =
115 "<!--\n"
116 " Service manifest for the %s service.\n"
117 "\n"
118 " Generated by inetconv(1M) from inetd.conf(4).\n"
119 "-->\n\n";
120
121 static const char xml_service_bundle[] =
122 "<service_bundle type='manifest' name='inetconv:%s'>\n\n";
123
124 static const char xml_service_name[] =
125 "<service\n"
126 " name='network/%s'\n"
127 " type='service'\n"
128 " version='1'>\n\n";
129
130 static const char xml_dependency[] =
131 " <dependency\n"
132 " name='%s'\n"
133 " grouping='require_all'\n"
134 " restart_on='restart'\n"
135 " type='service'>\n"
136 " <service_fmri value='%s' />\n"
137 " </dependency>\n\n";
138
139 static const char xml_instance[] =
140 " <create_default_instance enabled='true'/>\n\n";
141
142 static const char xml_restarter[] =
143 " <restarter>\n"
144 " <service_fmri value='%s' />\n"
145 " </restarter>\n\n";
146
147 static const char xml_exec_method_start[] =
148 " <!--\n"
149 " Set a timeout of 0 to signify to inetd that we don't want to\n"
150 " timeout this service, since the forked process is the one that\n"
151 " does the service's work. This is the case for most/all legacy\n"
152 " inetd services; for services written to take advantage of SMF\n"
153 " capabilities, the start method should fork off a process to\n"
154 " handle the request and return a success code.\n"
155 " -->\n"
156 " <exec_method\n"
157 " type='method'\n"
158 " name='%s'\n"
159 " %s='%s'\n"
160 " timeout_seconds='0'>\n"
161 " <method_context>\n"
162 " <method_credential %s='%s' group='%s' />\n"
163 " </method_context>\n";
164
165 static const char xml_arg0[] =
166 " <propval name='%s' type='astring'\n"
167 " value='%s' />\n";
168
169 static const char xml_exec_method_end[] =
170 " </exec_method>\n\n";
171
172 static const char xml_exec_method_disable[] =
173 " <!--\n"
174 " Use inetd's built-in kill support to disable services.\n"
175 " -->\n"
176 " <exec_method\n"
177 " type='method'\n"
178 " name='%s'\n"
179 " %s=':kill'\n"
180 " timeout_seconds='0'>\n";
181
182 static const char xml_exec_method_offline[] =
183 " <!--\n"
184 " Use inetd's built-in process kill support to offline wait type\n"
185 " services.\n"
186 " -->\n"
187 " <exec_method\n"
188 " type='method'\n"
189 " name='%s'\n"
190 " %s=':kill_process'\n"
191 " timeout_seconds='0'>\n";
192
193 static const char xml_inetconv_group_start[] =
194 " <!--\n"
195 " This property group is used to record information about\n"
196 " how this manifest was created. It is an implementation\n"
197 " detail which should not be modified or deleted.\n"
198 " -->\n"
199 " <property_group name='%s' type='framework'>\n"
200 " <propval name='%s' type='boolean' value='%s' />\n"
201 " <propval name='%s' type='integer' value='%d' />\n"
202 " <propval name='%s' type='astring' value=\n"
203 "'%s %s %s %s %s %s%s%s'\n"
204 " />\n";
205
206 static const char xml_property_group_start[] =
207 " <property_group name='%s' type='framework'>\n"
208 " <propval name='%s' type='astring' value='%s' />\n"
209 " <propval name='%s' type='astring' value='%s' />\n"
210 " <propval name='%s' type='astring' value='%s' />\n"
211 " <propval name='%s' type='boolean' value='%s' />\n"
212 " <propval name='%s' type='boolean' value='%s' />\n";
213
214 static const char xml_property_group_rpc[] =
215 " <propval name='%s' type='integer' value='%d' />\n"
216 " <propval name='%s' type='integer' value='%d' />"
217 "\n";
218
219 static const char xml_property_group_end[] =
220 " </property_group>\n\n";
221
222 static const char xml_stability[] =
223 " <stability value='External' />\n\n";
224
225 static const char xml_template[] =
226 " <template>\n"
227 " <common_name>\n"
228 " <loctext xml:lang='C'>\n"
229 "%s\n"
230 " </loctext>\n"
231 " </common_name>\n"
232 " </template>\n";
233
234 static const char xml_footer[] =
235 "</service>\n"
236 "\n"
237 "</service_bundle>\n";
238 /* end of manifest XML template strings */
239
240 static void *
safe_malloc(size_t size)241 safe_malloc(size_t size)
242 {
243 void *cp;
244
245 if ((cp = malloc(size)) == NULL) {
246 (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
247 progname, strerror(errno));
248 exit(EXIT_ERROR_SYS);
249 }
250 return (cp);
251 }
252
253 static char *
safe_strdup(char * s)254 safe_strdup(char *s)
255 {
256 char *cp;
257
258 if ((cp = strdup(s)) == NULL) {
259 (void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
260 progname, strerror(errno));
261 exit(EXIT_ERROR_SYS);
262 }
263 return (cp);
264 }
265
266 static char *
propertyname(char * name,char * prefix)267 propertyname(char *name, char *prefix)
268 {
269 static char *buf;
270 size_t len;
271 int c;
272 char *cp;
273
274 /* free any memory allocated by a previous call */
275 free(buf);
276
277 len = strlen(name) + strlen(prefix) + 1;
278 buf = safe_malloc(len);
279 buf[0] = '\0';
280
281 /*
282 * Property names must match the regular expression:
283 * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
284 */
285
286 /*
287 * Make sure the first character is alphabetic, if not insert prefix.
288 * Can't use isalpha() here as it's locale dependent but the property
289 * name regular expression isn't.
290 */
291 c = name[0];
292 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
293 (void) strlcat(buf, prefix, len);
294 }
295 (void) strlcat(buf, name, len);
296
297 /* convert any disallowed characters into '_' */
298 for (cp = buf; *cp != '\0'; cp++) {
299 if ((*cp < 'A' || *cp > 'Z') && (*cp < 'a' || *cp > 'z') &&
300 (*cp < '0' || *cp > '9') && (*cp != '.') && (*cp != '-'))
301 *cp = '_';
302 }
303 return (buf);
304 }
305
306 static char *
servicename(struct inetconfent * iconf)307 servicename(struct inetconfent *iconf)
308 {
309 static char *buf;
310 size_t len;
311 char *cp, *proto;
312
313 /* free any memory allocated by a previous call */
314 free(buf);
315
316 len = strlen(iconf->service) + strlen(iconf->protocol) +
317 sizeof ("rpc-/visible");
318 buf = safe_malloc(len);
319
320 /*
321 * Combine the service and protocol fields to produce a unique
322 * manifest service name. The syntax of a service name is:
323 * prop(/prop)*
324 */
325 (void) strlcpy(buf, propertyname(iconf->service,
326 iconf->isrpc ? "rpc-": "s-"), len);
327 (void) strlcat(buf, "/", len);
328
329 proto = iconf->protocol;
330 if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
331 proto = "rpc/visible";
332
333 /*
334 * SMF service names may not contain '.', but IANA services do
335 * allow its use, and property names can contain '.' as returned
336 * by propertyname(). So if the resultant SMF service name
337 * would contain a '.' we fix it here.
338 */
339 for (cp = buf; *cp != '\0'; cp++) {
340 if (*cp == '.')
341 *cp = '_';
342 }
343 (void) strlcat(buf, propertyname(proto, "p-"), len);
344 return (buf);
345 }
346
347 static boolean_t
is_v6only(char * protocol)348 is_v6only(char *protocol)
349 {
350 /* returns true if protocol is an IPv6 only protocol */
351 if ((strcmp(protocol, SOCKET_PROTO_TCP6_ONLY) == 0) ||
352 (strcmp(protocol, SOCKET_PROTO_UDP6_ONLY) == 0))
353 return (B_TRUE);
354 return (B_FALSE);
355 }
356
357 static char *
invalid_props(inetd_prop_t * p)358 invalid_props(inetd_prop_t *p)
359 {
360 static char
361 buf[sizeof (" service-name endpoint-type protocol wait-status")];
362
363 buf[0] = '\0';
364 if ((p[PT_SVC_NAME_INDEX].ip_error == IVE_INVALID) ||
365 (p[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
366 (p[PT_RPC_LW_VER_INDEX].ip_error == IVE_INVALID) ||
367 (p[PT_RPC_HI_VER_INDEX].ip_error == IVE_INVALID))
368 (void) strlcat(buf, " service-name", sizeof (buf));
369 if ((p[PT_SOCK_TYPE_INDEX].ip_error == IVE_INVALID) ||
370 (p[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET))
371 (void) strlcat(buf, " endpoint-type", sizeof (buf));
372 if ((p[PT_PROTO_INDEX].ip_error == IVE_INVALID) ||
373 (p[PT_PROTO_INDEX].ip_error == IVE_UNSET) ||
374 (p[PT_ISRPC_INDEX].ip_error == IVE_INVALID))
375 (void) strlcat(buf, " protocol", sizeof (buf));
376 if (p[PT_ISWAIT_INDEX].ip_error == IVE_INVALID)
377 (void) strlcat(buf, " wait-status", sizeof (buf));
378 return (buf);
379 }
380
381 static boolean_t
valid_basic_properties(struct inetconfent * iconf,struct fileinfo * finfo)382 valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
383 {
384 size_t prop_size;
385 inetd_prop_t *prop, *inetd_properties;
386 boolean_t valid = B_TRUE;
387 char *proto = iconf->protocol;
388 char *svc_name = iconf->service;
389
390 inetd_properties = get_prop_table(&prop_size);
391 prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
392 (void) memcpy(prop, inetd_properties,
393 prop_size * sizeof (inetd_prop_t));
394
395 put_prop_value_boolean(prop, PR_ISRPC_NAME, iconf->isrpc);
396 put_prop_value_boolean(prop, PR_ISWAIT_NAME, iconf->wait);
397 if (iconf->isrpc) {
398 put_prop_value_int(prop, PR_RPC_LW_VER_NAME,
399 iconf->rpc_low_version);
400 put_prop_value_int(prop, PR_RPC_HI_VER_NAME,
401 iconf->rpc_high_version);
402 svc_name = iconf->rpc_prog;
403 proto += 4; /* skip 'rpc/' */
404 }
405
406 if (!put_prop_value_string(prop, PR_SOCK_TYPE_NAME, iconf->endpoint) ||
407 !put_prop_value_string(prop, PR_SVC_NAME_NAME, svc_name)) {
408 valid = B_FALSE;
409
410 if (errno == ENOMEM) {
411 (void) fprintf(stderr,
412 gettext("%s: failed to allocate memory: %s\n"),
413 progname, strerror(errno));
414 exit(EXIT_ERROR_SYS);
415 }
416 }
417
418 put_prop_value_string_list(prop, PR_PROTO_NAME, get_protos(proto));
419
420 if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
421 valid = B_FALSE;
422 (void) fprintf(stderr, gettext("%s: Error %s line %d "
423 "invalid or inconsistent fields:%s\n"), progname,
424 finfo->filename, finfo->lineno,
425 invalid_props(prop));
426 }
427
428 free_instance_props(prop);
429 return (valid);
430 }
431
432 static boolean_t
valid_inetconfent(struct inetconfent * iconf,struct fileinfo * finfo)433 valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
434 {
435 boolean_t valid = B_TRUE;
436 size_t len;
437 char *cp, *endp;
438 struct passwd *pwd;
439 struct group *grp;
440 struct stat statb;
441 char *proto = iconf->protocol;
442
443 iconf->isrpc = B_FALSE;
444 if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
445 iconf->isrpc = B_TRUE;
446 iconf->rpc_prog = safe_strdup(iconf->service);
447
448 /* set RPC version numbers */
449 iconf->rpc_low_version = 1;
450 iconf->rpc_high_version = 1;
451 if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
452 *cp = '\0';
453 if (*++cp != '\0') {
454 errno = 0;
455 iconf->rpc_low_version = strtol(cp, &endp, 10);
456 if (errno != 0)
457 goto vererr;
458 cp = endp;
459 if (*cp == '-') {
460 if (*++cp == '\0')
461 goto vererr;
462 errno = 0;
463 iconf->rpc_high_version = strtol(cp,
464 &endp, 10);
465 if ((errno != 0) || (*endp != '\0'))
466 goto vererr;
467 } else if (*cp == '\0') {
468 iconf->rpc_high_version =
469 iconf->rpc_low_version;
470 } else {
471 vererr:
472 (void) fprintf(stderr, gettext(
473 "%s: Error %s line %d invalid RPC "
474 "version in service: %s\n"),
475 progname, finfo->filename,
476 finfo->lineno, iconf->service);
477 valid = B_FALSE;
478 }
479 }
480 }
481 proto += 4; /* skip 'rpc/' */
482 }
483 /* tcp6only and udp6only are not valid in inetd.conf */
484 if (is_v6only(proto)) {
485 (void) fprintf(stderr, gettext("%s: Error %s line %d "
486 "invalid protocol: %s\n"), progname,
487 finfo->filename, finfo->lineno, proto);
488 valid = B_FALSE;
489 }
490
491 if (strcmp(iconf->wait_status, "wait") == 0) {
492 iconf->wait = B_TRUE;
493 } else if (strcmp(iconf->wait_status, "nowait") == 0) {
494 iconf->wait = B_FALSE;
495 } else {
496 (void) fprintf(stderr,
497 gettext("%s: Error %s line %d invalid wait-status: %s\n"),
498 progname, finfo->filename, finfo->lineno,
499 iconf->wait_status);
500 valid = B_FALSE;
501 }
502
503 /* look up the username to set the groupname */
504 if ((pwd = getpwnam(iconf->username)) == NULL) {
505 (void) fprintf(stderr,
506 gettext("%s: Error %s line %d unknown user: %s\n"),
507 progname, finfo->filename, finfo->lineno,
508 iconf->username);
509 valid = B_FALSE;
510 } else {
511 if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
512 iconf->groupname = safe_strdup(grp->gr_name);
513 } else {
514 /* use the group ID if no groupname */
515 char s[1];
516
517 len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
518 iconf->groupname = safe_malloc(len);
519 (void) snprintf(iconf->groupname, len, "%d",
520 pwd->pw_gid);
521 }
522 }
523
524 /* check for internal services */
525 if (strcmp(iconf->server_program, "internal") == 0) {
526 valid = B_FALSE;
527 if ((strcmp(iconf->service, "echo") == 0) ||
528 (strcmp(iconf->service, "discard") == 0) ||
529 (strcmp(iconf->service, "time") == 0) ||
530 (strcmp(iconf->service, "daytime") == 0) ||
531 (strcmp(iconf->service, "chargen") == 0)) {
532 (void) fprintf(stderr, gettext(
533 "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
534 " packages contain the internal services\n"),
535 progname, finfo->filename, finfo->lineno);
536 } else {
537 (void) fprintf(stderr, gettext("%s: Error %s line %d "
538 "unknown internal service: %s\n"), progname,
539 finfo->filename, finfo->lineno, iconf->service);
540 }
541 } else if ((stat(iconf->server_program, &statb) == -1) &&
542 (errno == ENOENT)) {
543 (void) fprintf(stderr, gettext(
544 "%s: Error %s line %d server-program not found: %s\n"),
545 progname, finfo->filename, finfo->lineno,
546 iconf->server_program);
547 valid = B_FALSE;
548 }
549
550 return (valid && valid_basic_properties(iconf, finfo));
551 }
552
553 static void
free_inetconfent(struct inetconfent * iconf)554 free_inetconfent(struct inetconfent *iconf)
555 {
556 if (iconf == NULL)
557 return;
558
559 free(iconf->service);
560 free(iconf->endpoint);
561 free(iconf->protocol);
562 free(iconf->wait_status);
563 free(iconf->username);
564 free(iconf->server_program);
565 free(iconf->server_args);
566 free(iconf->rpc_prog);
567 free(iconf->groupname);
568 free(iconf->exec);
569 free(iconf->arg0);
570
571 free(iconf);
572 }
573
574 static struct inetconfent *
line_to_inetconfent(char * line)575 line_to_inetconfent(char *line)
576 {
577 char *cp;
578 struct inetconfent *iconf;
579
580 iconf = safe_malloc(sizeof (struct inetconfent));
581 (void) memset(iconf, 0, sizeof (struct inetconfent));
582
583 if ((cp = strtok(line, " \t\n")) == NULL)
584 goto fail;
585 iconf->service = safe_strdup(cp);
586
587 if ((cp = strtok(NULL, " \t\n")) == NULL)
588 goto fail;
589 iconf->endpoint = safe_strdup(cp);
590
591 if ((cp = strtok(NULL, " \t\n")) == NULL)
592 goto fail;
593 iconf->protocol = safe_strdup(cp);
594
595 if ((cp = strtok(NULL, " \t\n")) == NULL)
596 goto fail;
597 iconf->wait_status = safe_strdup(cp);
598
599 if ((cp = strtok(NULL, " \t\n")) == NULL)
600 goto fail;
601 iconf->username = safe_strdup(cp);
602
603 if ((cp = strtok(NULL, " \t\n")) == NULL)
604 goto fail;
605 iconf->server_program = safe_strdup(cp);
606
607 /* last field is optional */
608 if ((cp = strtok(NULL, "\n")) != NULL)
609 iconf->server_args = safe_strdup(cp);
610
611 /* Combine args and server name to construct exec and args fields */
612 if (iconf->server_args == NULL) {
613 iconf->exec = safe_strdup(iconf->server_program);
614 } else {
615 int len;
616 char *args, *endp;
617
618 len = strlen(iconf->server_program) +
619 strlen(iconf->server_args) + 1;
620 iconf->exec = safe_malloc(len);
621 (void) strlcpy(iconf->exec, iconf->server_program, len);
622
623 args = safe_strdup(iconf->server_args);
624 if ((cp = strtok(args, " \t")) != NULL) {
625 if ((endp = strrchr(iconf->exec, '/')) == NULL)
626 endp = iconf->exec;
627 else
628 endp++;
629 /* only set arg0 property value if needed */
630 if (strcmp(endp, cp) != 0)
631 iconf->arg0 = safe_strdup(cp);
632 while ((cp = strtok(NULL, " \t")) != NULL) {
633 (void) strlcat(iconf->exec, " ", len);
634 (void) strlcat(iconf->exec, cp, len);
635 }
636 }
637 free(args);
638 }
639
640 return (iconf);
641 fail:
642 free_inetconfent(iconf);
643 return (NULL);
644 }
645
646 static void
skipline(FILE * fp)647 skipline(FILE *fp)
648 {
649 int c;
650
651 /* skip remainder of a line */
652 while (((c = getc(fp)) != EOF) && (c != '\n'))
653 ;
654 }
655
656 static struct inetconfent *
fgetinetconfent(struct fileinfo * finfo,boolean_t validate)657 fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
658 {
659 char *cp;
660 struct inetconfent *iconf;
661 char line[MAX_SRC_LINELEN];
662
663 while (fgets(line, sizeof (line), finfo->fp) != NULL) {
664 finfo->lineno++;
665
666 /* skip empty or commented out lines */
667 if (*line == '\n')
668 continue;
669 if (*line == '#') {
670 if (line[strlen(line) - 1] != '\n')
671 skipline(finfo->fp);
672 continue;
673 }
674 /* check for lines which are too long */
675 if (line[strlen(line) - 1] != '\n') {
676 (void) fprintf(stderr,
677 gettext("%s: Error %s line %d too long, skipped\n"),
678 progname, finfo->filename, finfo->lineno);
679 skipline(finfo->fp);
680 finfo->failcnt++;
681 continue;
682 }
683 /* remove in line comments and newline character */
684 if ((cp = strchr(line, '#')) == NULL)
685 cp = strchr(line, '\n');
686 if (cp)
687 *cp = '\0';
688
689 if ((iconf = line_to_inetconfent(line)) == NULL) {
690 (void) fprintf(stderr, gettext(
691 "%s: Error %s line %d too few fields, skipped\n"),
692 progname, finfo->filename, finfo->lineno);
693 finfo->failcnt++;
694 continue;
695 }
696
697 if (!validate || valid_inetconfent(iconf, finfo))
698 return (iconf);
699
700 finfo->failcnt++;
701 free_inetconfent(iconf);
702 }
703 return (NULL);
704 }
705
706 static char *
boolstr(boolean_t val)707 boolstr(boolean_t val)
708 {
709 if (val)
710 return ("true");
711 return ("false");
712 }
713
714 static int
print_manifest(FILE * f,char * filename,struct inetconfent * iconf)715 print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
716 {
717 if (fprintf(f, xml_header) < 0)
718 goto print_err;
719
720 if (fprintf(f, xml_comment,
721 iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
722 goto print_err;
723
724 if (fprintf(f, xml_service_bundle, iconf->service) < 0)
725 goto print_err;
726 if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
727 goto print_err;
728 if (fprintf(f, xml_instance) < 0)
729 goto print_err;
730 if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
731 goto print_err;
732 if (iconf->isrpc) {
733 if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_FMRI) < 0)
734 goto print_err;
735 }
736
737 if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
738 iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
739 goto print_err;
740 if (iconf->arg0 != NULL) {
741 if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
742 goto print_err;
743 }
744 if (fprintf(f, xml_exec_method_end) < 0)
745 goto print_err;
746
747 if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
748 PR_EXEC_NAME) < 0)
749 goto print_err;
750 if (fprintf(f, xml_exec_method_end) < 0)
751 goto print_err;
752
753 if (iconf->wait) {
754 if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
755 PR_EXEC_NAME) < 0)
756 goto print_err;
757 if (fprintf(f, xml_exec_method_end) < 0)
758 goto print_err;
759 }
760
761 if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
762 PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
763 PR_VERSION_NAME, INETCONV_VERSION,
764 PR_SOURCE_LINE_NAME, iconf->service,
765 iconf->endpoint, iconf->protocol, iconf->wait_status,
766 iconf->username, iconf->server_program,
767 iconf->server_args == NULL ? "" : " ",
768 iconf->server_args == NULL ? "" : iconf->server_args) < 0)
769 goto print_err;
770 if (fprintf(f, xml_property_group_end) < 0)
771 goto print_err;
772
773 if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
774 PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
775 PR_SOCK_TYPE_NAME, iconf->endpoint,
776 PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
777 iconf->protocol,
778 PR_ISWAIT_NAME, boolstr(iconf->wait),
779 PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
780 goto print_err;
781 if (iconf->isrpc) {
782 if (fprintf(f, xml_property_group_rpc,
783 PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
784 PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
785 goto print_err;
786 }
787 if (fprintf(f, xml_property_group_end) < 0)
788 goto print_err;
789
790 if (fprintf(f, xml_stability) < 0)
791 goto print_err;
792 if (fprintf(f, xml_template,
793 iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
794 goto print_err;
795 if (fprintf(f, xml_footer) < 0)
796 goto print_err;
797
798 (void) printf("%s -> %s\n", iconf->service, filename);
799 return (0);
800
801 print_err:
802 (void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
803 progname, filename, strerror(errno));
804 return (-1);
805 }
806
807 static struct fileinfo *
open_srcfile(char * filename)808 open_srcfile(char *filename)
809 {
810 struct fileinfo *finfo = NULL;
811 FILE *fp;
812
813 if (filename != NULL) {
814 if ((fp = fopen(filename, "r")) == NULL) {
815 (void) fprintf(stderr,
816 gettext("%s: Error opening %s: %s\n"),
817 progname, filename, strerror(errno));
818 }
819 } else {
820 /*
821 * If no source file specified, do the same as inetd and first
822 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
823 */
824 filename = MAIN_CONFIG;
825 if ((fp = fopen(filename, "r")) == NULL) {
826 (void) fprintf(stderr,
827 gettext("%s: Error opening %s: %s\n"),
828 progname, filename, strerror(errno));
829 filename = ALT_CONFIG;
830 if ((fp = fopen(filename, "r")) == NULL) {
831 (void) fprintf(stderr, gettext(
832 "%s: Error opening %s: %s\n"), progname,
833 filename, strerror(errno));
834 }
835 }
836 }
837 if (fp != NULL) {
838 finfo = safe_malloc(sizeof (struct fileinfo));
839 finfo->fp = fp;
840 finfo->filename = filename;
841 finfo->lineno = 0;
842 finfo->failcnt = 0;
843 (void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
844 }
845 return (finfo);
846 }
847
848 /*
849 * Opens manifest output file. Returns 0 on success, -1 if the file
850 * exists, -2 on other errors.
851 */
852 static int
open_dstfile(char * destdir,boolean_t overwrite,struct inetconfent * iconf,struct fileinfo ** finfo)853 open_dstfile(
854 char *destdir,
855 boolean_t overwrite,
856 struct inetconfent *iconf,
857 struct fileinfo **finfo)
858 {
859 int fd;
860 size_t len;
861 char *dstfile, *cp, *proto;
862 FILE *fp;
863
864 /* if no destdir specified, use appropriate default */
865 if (destdir == NULL) {
866 if (iconf->isrpc)
867 destdir = MANIFEST_RPC_DIR;
868 else
869 destdir = MANIFEST_DIR;
870 }
871
872 len = strlen(destdir) + strlen(iconf->service) +
873 strlen(iconf->protocol) + sizeof ("/-visible.xml");
874 dstfile = safe_malloc(len);
875
876 (void) strlcpy(dstfile, destdir, len);
877 if (dstfile[strlen(dstfile) - 1] != '/')
878 (void) strlcat(dstfile, "/", len);
879 cp = dstfile + strlen(dstfile);
880
881 (void) strlcat(dstfile, iconf->service, len);
882 (void) strlcat(dstfile, "-", len);
883
884 proto = iconf->protocol;
885 if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
886 proto = "rpc/visible";
887
888 (void) strlcat(dstfile, proto, len);
889 (void) strlcat(dstfile, ".xml", len);
890
891 /* convert any '/' chars in service or protocol to '_' chars */
892 while ((cp = strchr(cp, '/')) != NULL)
893 *cp = '_';
894
895 fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
896 0644);
897 if (fd == -1) {
898 if (!overwrite && (errno == EEXIST)) {
899 (void) fprintf(stderr,
900 gettext("%s: Notice: Service manifest for "
901 "%s already generated as %s, skipped\n"),
902 progname, iconf->service, dstfile);
903 free(dstfile);
904 return (-1);
905 } else {
906 (void) fprintf(stderr,
907 gettext("%s: Error opening %s: %s\n"),
908 progname, dstfile, strerror(errno));
909 free(dstfile);
910 return (-2);
911 }
912 }
913 /* Clear errno to catch the "no stdio streams" case */
914 errno = 0;
915 if ((fp = fdopen(fd, "w")) == NULL) {
916 char *s = strerror(errno);
917 if (errno == 0)
918 s = gettext("No stdio streams available");
919 (void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
920 progname, s);
921 (void) close(fd);
922 free(dstfile);
923 return (-2);
924 }
925 *finfo = safe_malloc(sizeof (struct fileinfo));
926 (*finfo)->fp = fp;
927 (*finfo)->filename = dstfile;
928 (*finfo)->lineno = 0;
929 (*finfo)->failcnt = 0;
930 return (0);
931 }
932
933 static int
import_manifest(char * filename)934 import_manifest(char *filename)
935 {
936 int status;
937 pid_t pid, wpid;
938 char *cp;
939
940 if ((cp = strrchr(filename, '/')) == NULL)
941 cp = filename;
942 else
943 cp++;
944 (void) printf(gettext("Importing %s ..."), cp);
945
946 if ((pid = fork()) == -1) {
947 (void) fprintf(stderr,
948 gettext("\n%s: fork failed, %s not imported: %s\n"),
949 progname, filename, strerror(errno));
950 exit(EXIT_ERROR_SYS);
951 }
952 if (pid == 0) {
953 /* child */
954 (void) fclose(stdin);
955 (void) setenv("SVCCFG_CHECKHASH", "1", 1);
956 (void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
957 (void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
958 progname, SVCCFG_PATH, strerror(errno));
959 _exit(EXIT_ERROR_SYS);
960 }
961 /* parent */
962 if ((wpid = waitpid(pid, &status, 0)) != pid) {
963 (void) fprintf(stderr, gettext(
964 "\n%s: unexpected wait (%d) from import of %s: %s\n"),
965 progname, wpid, filename, strerror(errno));
966 return (-1);
967 }
968 if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
969 (void) fprintf(stderr,
970 gettext("\n%s: import failure (%d) for %s\n"),
971 progname, WEXITSTATUS(status), filename);
972 return (-1);
973 }
974 (void) printf(gettext("Done\n"));
975 return (0);
976 }
977
978 static int
inetd_config_path(char ** path)979 inetd_config_path(char **path)
980 {
981 int fd;
982 char *arg1, *configfile, *configstr;
983 scf_simple_prop_t *sp;
984 char cpath[PATH_MAX];
985
986 if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
987 SCF_PROPERTY_EXEC)) == NULL)
988 return (-1);
989 if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
990 scf_simple_prop_free(sp);
991 return (-1);
992 }
993 configstr = safe_strdup(configstr);
994 scf_simple_prop_free(sp);
995
996 /*
997 * Look for the optional configuration file, the syntax is:
998 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
999 */
1000 if (strtok(configstr, " \t") == NULL) {
1001 free(configstr);
1002 return (-1);
1003 }
1004 if ((arg1 = strtok(NULL, " \t")) == NULL) {
1005 free(configstr);
1006 return (-1);
1007 }
1008 if (strtok(NULL, " \t") == NULL) {
1009 /*
1010 * No configuration file specified, do the same as inetd and
1011 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
1012 */
1013 configfile = MAIN_CONFIG;
1014 if ((fd = open(configfile, O_RDONLY)) >= 0)
1015 (void) close(fd);
1016 else
1017 configfile = ALT_CONFIG;
1018
1019 } else {
1020 /* make sure there are no more arguments */
1021 if (strtok(NULL, " \t") != NULL) {
1022 free(configstr);
1023 return (-1);
1024 }
1025 configfile = arg1;
1026 }
1027
1028 /* configuration file must be an absolute pathname */
1029 if (*configfile != '/') {
1030 free(configstr);
1031 return (-1);
1032 }
1033
1034 if (realpath(configfile, cpath) == NULL)
1035 (void) strlcpy(cpath, configfile, sizeof (cpath));
1036
1037 free(configstr);
1038 *path = safe_strdup(cpath);
1039 return (0);
1040 }
1041
1042 static int
update_hash(char * srcfile)1043 update_hash(char *srcfile)
1044 {
1045 scf_error_t rval;
1046 char *inetd_cpath, *hashstr;
1047 char cpath[PATH_MAX];
1048
1049 /* determine the config file inetd is using */
1050 if (inetd_config_path(&inetd_cpath) == -1) {
1051 (void) fprintf(stderr,
1052 gettext("%s: Error reading from repository\n"), progname);
1053 return (-1);
1054 }
1055
1056 /* resolve inetconv input filename */
1057 if (realpath(srcfile, cpath) == NULL)
1058 (void) strlcpy(cpath, srcfile, sizeof (cpath));
1059
1060 /* if inetconv and inetd are using the same config file, update hash */
1061 if (strcmp(cpath, inetd_cpath) != 0) {
1062 free(inetd_cpath);
1063 return (0);
1064 }
1065 free(inetd_cpath);
1066
1067 /* generic error message as use of hash is not exposed to the user */
1068 if (calculate_hash(cpath, &hashstr) != 0) {
1069 (void) fprintf(stderr,
1070 gettext("%s: Error unable to update repository\n"),
1071 progname);
1072 return (-1);
1073 }
1074 /* generic error message as use of hash is not exposed to the user */
1075 if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
1076 (void) fprintf(stderr,
1077 gettext("%s: Error updating repository: %s\n"),
1078 progname, scf_strerror(rval));
1079 free(hashstr);
1080 return (-1);
1081 }
1082 free(hashstr);
1083 return (0);
1084 }
1085
1086 static void
property_error(const char * fmri,const char * prop)1087 property_error(const char *fmri, const char *prop)
1088 {
1089 (void) fprintf(stderr,
1090 gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
1091 fmri, prop);
1092 }
1093
1094 /*
1095 * modify_sprop takes a handle, an instance, a property group, a property,
1096 * and an astring value, and modifies the instance (or service's) specified
1097 * property in the repository to the submitted value.
1098 *
1099 * returns -1 on error, 1 on successful transaction completion.
1100 */
1101
1102 static int
modify_sprop(scf_handle_t * h,const scf_instance_t * inst,const char * pg,const char * prop,const char * value)1103 modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
1104 const char *pg, const char *prop, const char *value)
1105 {
1106 scf_transaction_t *tx = NULL;
1107 scf_transaction_entry_t *ent = NULL;
1108 scf_propertygroup_t *gpg = NULL;
1109 scf_property_t *eprop = NULL;
1110 scf_value_t *v = NULL;
1111 scf_service_t *svc = NULL;
1112 int ret = 0, create = 0;
1113
1114 if ((gpg = scf_pg_create(h)) == NULL)
1115 return (-1);
1116
1117 /* Get the property group */
1118 if (scf_instance_get_pg(inst, pg, gpg) == -1) {
1119 /* Not a property of the instance, try the service instead */
1120 if ((svc = scf_service_create(h)) == NULL) {
1121 ret = -1;
1122 goto out;
1123 }
1124 if ((scf_instance_get_parent(inst, svc) == -1) ||
1125 (scf_service_get_pg(svc, pg, gpg) == -1)) {
1126 ret = -1;
1127 goto out;
1128 }
1129 }
1130
1131 if ((eprop = scf_property_create(h)) == NULL) {
1132 ret = -1;
1133 goto out;
1134 }
1135
1136 if (scf_pg_get_property(gpg, prop, eprop) == -1) {
1137 if (scf_error() != SCF_ERROR_NOT_FOUND) {
1138 ret = -1;
1139 goto out;
1140 }
1141
1142 create = 1;
1143 }
1144
1145 if ((tx = scf_transaction_create(h)) == NULL ||
1146 (ent = scf_entry_create(h)) == NULL) {
1147 ret = -1;
1148 goto out;
1149 }
1150
1151 do {
1152 if (scf_transaction_start(tx, gpg) == -1) {
1153 ret = -1;
1154 goto out;
1155 }
1156
1157 /* Modify the property */
1158 if (create)
1159 ret = scf_transaction_property_new(tx, ent, prop,
1160 SCF_TYPE_ASTRING);
1161 else
1162 ret = scf_transaction_property_change_type(tx, ent,
1163 prop, SCF_TYPE_ASTRING);
1164
1165 if (ret == -1)
1166 goto out;
1167
1168 if ((v = scf_value_create(h)) == NULL) {
1169 ret = -1;
1170 goto out;
1171 }
1172
1173 if (scf_value_set_astring(v, value) == -1) {
1174 ret = -1;
1175 goto out;
1176 }
1177
1178 if (scf_entry_add_value(ent, v) == -1) {
1179 ret = -1;
1180 goto out;
1181 }
1182
1183 ret = scf_transaction_commit(tx);
1184
1185 if (ret == 0) {
1186 /* Property group was stale, retry */
1187 if (scf_pg_update(gpg) == -1) {
1188 ret = -1;
1189 goto out;
1190 }
1191 scf_transaction_reset(tx);
1192 }
1193
1194 } while (ret == 0);
1195 out:
1196 scf_value_destroy(v);
1197 scf_entry_destroy(ent);
1198 scf_transaction_destroy(tx);
1199 scf_property_destroy(eprop);
1200 scf_service_destroy(svc);
1201 scf_pg_destroy(gpg);
1202
1203 return (ret);
1204 }
1205
1206 /*
1207 * list_callback is the callback function to be handed to simple_walk_instances
1208 * in main. It is called once on every instance on a machine. If that
1209 * instance is controlled by inetd, we test whether it's the same
1210 * service that we're looking at from the inetd.conf file, and enable it if
1211 * they are the same.
1212 */
1213
1214 /*ARGSUSED*/
1215 static int
list_callback(scf_handle_t * h,scf_instance_t * inst,void * buf)1216 list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
1217 {
1218 ssize_t max_name_length;
1219 char *svc_name;
1220 scf_simple_prop_t *prop = NULL;
1221 scf_simple_prop_t *sockprop = NULL;
1222 scf_simple_prop_t *rpcprop = NULL;
1223 scf_simple_prop_t *progprop = NULL;
1224 const char *name, *endpoint, *restart_str, *prog;
1225 struct inetconfent *iconf = (struct inetconfent *)buf;
1226 uint8_t *isrpc;
1227
1228 max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1229 if ((svc_name = malloc(max_name_length + 1)) == NULL) {
1230 (void) fprintf(stderr, gettext("Error: Out of memory.\n"));
1231 return (SCF_FAILED);
1232 }
1233
1234 /*
1235 * Get the FMRI of the instance, and check if its delegated restarter
1236 * is inetd. A missing or empty restarter property implies that
1237 * svc.startd is the restarter.
1238 */
1239
1240 if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
1241 (void) fprintf(stderr,
1242 gettext("Error: Unable to obtain FMRI for service %1$s."),
1243 svc_name);
1244 free(svc_name);
1245 return (SCF_FAILED);
1246 }
1247
1248 if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
1249 SCF_PROPERTY_RESTARTER)) == NULL)
1250 goto out;
1251
1252 if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
1253 goto out;
1254
1255 if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
1256 goto out;
1257
1258 /* Free restarter prop so it can be reused below */
1259 scf_simple_prop_free(prop);
1260
1261 /*
1262 * We know that this instance is managed by inetd.
1263 * Now get the properties needed to decide if it matches this
1264 * line in the old config file.
1265 */
1266
1267 if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
1268 PR_SVC_NAME_NAME)) == NULL) ||
1269 ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
1270 property_error(svc_name, PR_SVC_NAME_NAME);
1271 goto out;
1272 }
1273
1274 if (((sockprop = scf_simple_prop_get(h, svc_name,
1275 PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
1276 ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
1277 property_error(svc_name, PR_SOCK_TYPE_NAME);
1278 goto out;
1279 }
1280
1281 if (((rpcprop = scf_simple_prop_get(h, svc_name,
1282 PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
1283 ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
1284 property_error(svc_name, PR_ISRPC_NAME);
1285 goto out;
1286 }
1287
1288 if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
1289 PR_EXEC_NAME)) == NULL) ||
1290 ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
1291 property_error(svc_name, PR_EXEC_NAME);
1292 }
1293
1294
1295 /* If it's RPC, we truncate off the version portion for comparison */
1296 if (*isrpc) {
1297 char *cp;
1298
1299 cp = strchr(iconf->service, '/');
1300 if (cp != NULL)
1301 *cp = '\0';
1302 }
1303
1304 /*
1305 * If name of this service and endpoint are equal to values from
1306 * iconf fields, and they're either both RPC or both non-RPC,
1307 * then we have a match; update the exec and arg0 properties if
1308 * necessary, then enable it.
1309 * We don't return an error if either operation fails so that we
1310 * continue to try all the other services.
1311 */
1312 if (strcmp(name, iconf->service) == 0 &&
1313 strcmp(endpoint, iconf->endpoint) == 0 &&
1314 *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
1315 /* Can't update exec on internal services */
1316 if ((strcmp(iconf->server_program, "internal") != 0) &&
1317 (strcmp(iconf->exec, prog) != 0)) {
1318 /* User had edited the command */
1319 if (!import) {
1320 /* Dry run only */
1321 (void) printf(
1322 gettext("Would update %s to %s %s"),
1323 svc_name, PR_EXEC_NAME, iconf->exec);
1324 if (iconf->arg0 != NULL) {
1325 (void) printf(
1326 gettext(" with %s of %s\n"),
1327 PR_ARG0_NAME, iconf->arg0);
1328 } else {
1329 (void) printf("\n");
1330 }
1331 } else {
1332 /* Update instance's exec property */
1333 if (modify_sprop(h, inst, START_METHOD_NAME,
1334 PR_EXEC_NAME, iconf->exec) != 1)
1335 (void) fprintf(stderr,
1336 gettext("Error: Unable to update "
1337 "%s property of %s, %s\n"),
1338 PR_EXEC_NAME, svc_name,
1339 scf_strerror(scf_error()));
1340 else
1341 (void) printf("%s will %s %s\n",
1342 svc_name, PR_EXEC_NAME,
1343 iconf->exec);
1344
1345 /* Update arg0 prop, if needed */
1346 if (iconf->arg0 != NULL) {
1347 if (modify_sprop(h, inst,
1348 START_METHOD_NAME, PR_ARG0_NAME,
1349 iconf->arg0) != 1) {
1350 (void) fprintf(stderr,
1351 gettext("Error: Unable to "
1352 "update %s property of "
1353 "%s, %s\n"), PR_ARG0_NAME,
1354 svc_name,
1355 scf_strerror(scf_error()));
1356 } else {
1357 (void) printf("%s will have an "
1358 "%s of %s\n", svc_name,
1359 PR_ARG0_NAME, iconf->arg0);
1360 }
1361 }
1362 }
1363 }
1364
1365 if (!import) {
1366 /* Dry-run only */
1367 (void) printf("Would enable %s\n", svc_name);
1368 } else {
1369 if (smf_enable_instance(svc_name, 0) != 0)
1370 (void) fprintf(stderr,
1371 gettext("Error: Failed to enable %s\n"),
1372 svc_name);
1373 else
1374 (void) printf("%s enabled\n", svc_name);
1375 }
1376 }
1377
1378 out:
1379 free(svc_name);
1380 scf_simple_prop_free(prop);
1381 scf_simple_prop_free(sockprop);
1382 scf_simple_prop_free(rpcprop);
1383 scf_simple_prop_free(progprop);
1384 return (SCF_SUCCESS);
1385 }
1386
1387 static void
usage(void)1388 usage(void)
1389 {
1390 (void) fprintf(stderr, gettext(
1391 "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
1392 " %1$s -e [-n] [-i srcfile]\n"
1393 "-? Display this usage message\n"
1394 "-e Enable smf services which are enabled in the input\n"
1395 " file\n"
1396 "-f Force overwrite of existing manifests\n"
1397 "-n Do not import converted manifests,\n"
1398 " or only display services which would be enabled\n"
1399 "-i srcfile Alternate input file\n"
1400 "-o destdir Alternate output directory for manifests\n"),
1401 progname);
1402 exit(EXIT_USAGE);
1403 }
1404
1405 int
main(int argc,char * argv[])1406 main(int argc, char *argv[])
1407 {
1408 int c, rval, convert_err, import_err = 0, enable_err = 0;
1409 boolean_t overwrite = B_FALSE;
1410 boolean_t enable = B_FALSE;
1411 char *srcfile = NULL;
1412 char *destdir = NULL;
1413 struct fileinfo *srcfinfo, *dstfinfo;
1414 struct inetconfent *iconf;
1415
1416 setbuf(stdout, NULL);
1417 (void) setlocale(LC_ALL, "");
1418 (void) textdomain(TEXT_DOMAIN);
1419
1420 if ((progname = strrchr(argv[0], '/')) == NULL)
1421 progname = argv[0];
1422 else
1423 progname++;
1424
1425 while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
1426 switch (c) {
1427 case 'e':
1428 /* enable services based on existing file config */
1429 enable = B_TRUE;
1430 break;
1431
1432 case 'f':
1433 /* overwrite existing manifests */
1434 overwrite = B_TRUE;
1435 break;
1436 case 'n':
1437 /* don't import manifests, or dry-run enable */
1438 import = B_FALSE;
1439 break;
1440 case 'i':
1441 /* alternate input file */
1442 if (srcfile != NULL) {
1443 (void) fprintf(stderr,
1444 gettext("%s: Error only one -%c allowed\n"),
1445 progname, optopt);
1446 usage();
1447 }
1448 srcfile = optarg;
1449 break;
1450 case 'o':
1451 /* alternate output directory */
1452 if (destdir != NULL) {
1453 (void) fprintf(stderr,
1454 gettext("%s: Error only one -%c allowed\n"),
1455 progname, optopt);
1456 usage();
1457 }
1458 destdir = optarg;
1459 break;
1460 case '?': /*FALLTHROUGH*/
1461 default:
1462 usage();
1463 break;
1464 }
1465 }
1466
1467 /*
1468 * Display usage if extraneous args supplied or enable specified in
1469 * combination with overwrite or destdir
1470 */
1471 if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
1472 usage();
1473
1474 if ((srcfinfo = open_srcfile(srcfile)) == NULL)
1475 return (EXIT_ERROR_CONV);
1476
1477 while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
1478 /*
1479 * If we're enabling, then just walk all the services for each
1480 * line and enable those which match.
1481 */
1482 if (enable) {
1483 rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
1484 list_callback);
1485 free_inetconfent(iconf);
1486 if (rval == SCF_FAILED) {
1487 /* Only print msg if framework error */
1488 if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
1489 (void) fprintf(stderr, gettext(
1490 "Error walking instances: %s.\n"),
1491 scf_strerror(scf_error()));
1492 enable_err++;
1493 break;
1494 }
1495 continue;
1496 }
1497
1498 /* Remainder of loop used for conversion & import */
1499 if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
1500 < 0) {
1501 /*
1502 * Only increment error counter if the failure was
1503 * other than the file already existing.
1504 */
1505 if (rval == -2)
1506 srcfinfo->failcnt++;
1507 free_inetconfent(iconf);
1508 continue;
1509 }
1510 rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
1511 (void) fclose(dstfinfo->fp);
1512 if (rval == 0) {
1513 if (import &&
1514 (import_manifest(dstfinfo->filename) != 0))
1515 import_err++;
1516 } else {
1517 (void) unlink(dstfinfo->filename);
1518 srcfinfo->failcnt++;
1519 }
1520 free(dstfinfo->filename);
1521 free(dstfinfo);
1522 free_inetconfent(iconf);
1523 }
1524 (void) fclose(srcfinfo->fp);
1525 convert_err = srcfinfo->failcnt;
1526
1527 /* Update hash only if not in enable mode, and only if importing */
1528 if (!enable && import && (update_hash(srcfinfo->filename) != 0))
1529 import_err++;
1530
1531 free(srcfinfo);
1532
1533 if (enable_err != 0)
1534 return (EXIT_ERROR_ENBL);
1535 if (import_err != 0)
1536 return (EXIT_ERROR_IMP);
1537 if (convert_err != 0)
1538 return (EXIT_ERROR_CONV);
1539 return (EXIT_SUCCESS);
1540 }
1541