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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * inetconv - convert inetd.conf entries into smf(5) service manifests,
28 * import them into smf(5) repository
31 #include <sys/types.h>
32 #include <sys/param.h>
48 #include <rpc/nettype.h>
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 */
59 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
62 #define MAIN_CONFIG "/etc/inet/inetd.conf"
63 #define ALT_CONFIG "/etc/inetd.conf"
65 #define MANIFEST_DIR "/lib/svc/manifest/network"
66 #define MANIFEST_RPC_DIR MANIFEST_DIR "/rpc"
67 #define SVCCFG_PATH "/usr/sbin/svccfg"
69 #define RPCBIND_FMRI "svc:/network/rpc/bind"
71 /* maximum allowed length of an inetd.conf format line */
72 #define MAX_SRC_LINELEN 32768
74 /* Version of inetconv, used as a marker in services we generate */
75 #define INETCONV_VERSION 1
78 /* fields as read from inetd.conf format line */
86 /* information derived from above fields */
104 static char *progname
;
106 static boolean_t import
= B_TRUE
;
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";
114 static const char xml_comment
[] =
116 " Service manifest for the %s service.\n"
118 " Generated by inetconv(1M) from inetd.conf(4).\n"
121 static const char xml_service_bundle
[] =
122 "<service_bundle type='manifest' name='inetconv:%s'>\n\n";
124 static const char xml_service_name
[] =
126 " name='network/%s'\n"
130 static const char xml_dependency
[] =
133 " grouping='require_all'\n"
134 " restart_on='restart'\n"
136 " <service_fmri value='%s' />\n"
137 " </dependency>\n\n";
139 static const char xml_instance
[] =
140 " <create_default_instance enabled='true'/>\n\n";
142 static const char xml_restarter
[] =
144 " <service_fmri value='%s' />\n"
147 static const char xml_exec_method_start
[] =
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"
160 " timeout_seconds='0'>\n"
161 " <method_context>\n"
162 " <method_credential %s='%s' group='%s' />\n"
163 " </method_context>\n";
165 static const char xml_arg0
[] =
166 " <propval name='%s' type='astring'\n"
169 static const char xml_exec_method_end
[] =
170 " </exec_method>\n\n";
172 static const char xml_exec_method_disable
[] =
174 " Use inetd's built-in kill support to disable services.\n"
180 " timeout_seconds='0'>\n";
182 static const char xml_exec_method_offline
[] =
184 " Use inetd's built-in process kill support to offline wait type\n"
190 " %s=':kill_process'\n"
191 " timeout_seconds='0'>\n";
193 static const char xml_inetconv_group_start
[] =
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"
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"
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";
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' />"
219 static const char xml_property_group_end
[] =
220 " </property_group>\n\n";
222 static const char xml_stability
[] =
223 " <stability value='External' />\n\n";
225 static const char xml_template
[] =
228 " <loctext xml:lang='C'>\n"
234 static const char xml_footer
[] =
237 "</service_bundle>\n";
238 /* end of manifest XML template strings */
241 safe_malloc(size_t size
)
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
);
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
);
267 propertyname(char *name
, char *prefix
)
274 /* free any memory allocated by a previous call */
277 len
= strlen(name
) + strlen(prefix
) + 1;
278 buf
= safe_malloc(len
);
282 * Property names must match the regular expression:
283 * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
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.
292 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z')) {
293 (void) strlcat(buf
, prefix
, len
);
295 (void) strlcat(buf
, name
, len
);
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
!= '-'))
307 servicename(struct inetconfent
*iconf
)
313 /* free any memory allocated by a previous call */
316 len
= strlen(iconf
->service
) + strlen(iconf
->protocol
) +
317 sizeof ("rpc-/visible");
318 buf
= safe_malloc(len
);
321 * Combine the service and protocol fields to produce a unique
322 * manifest service name. The syntax of a service name is:
325 (void) strlcpy(buf
, propertyname(iconf
->service
,
326 iconf
->isrpc
? "rpc-": "s-"), len
);
327 (void) strlcat(buf
, "/", len
);
329 proto
= iconf
->protocol
;
330 if (iconf
->isrpc
&& (strcmp(iconf
->protocol
, "rpc/*") == 0))
331 proto
= "rpc/visible";
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.
339 for (cp
= buf
; *cp
!= '\0'; cp
++) {
343 (void) strlcat(buf
, propertyname(proto
, "p-"), len
);
348 is_v6only(char *protocol
)
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))
358 invalid_props(inetd_prop_t
*p
)
361 buf
[sizeof (" service-name endpoint-type protocol wait-status")];
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
));
382 valid_basic_properties(struct inetconfent
*iconf
, struct fileinfo
*finfo
)
385 inetd_prop_t
*prop
, *inetd_properties
;
386 boolean_t valid
= B_TRUE
;
387 char *proto
= iconf
->protocol
;
388 char *svc_name
= iconf
->service
;
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
));
395 put_prop_value_boolean(prop
, PR_ISRPC_NAME
, iconf
->isrpc
);
396 put_prop_value_boolean(prop
, PR_ISWAIT_NAME
, iconf
->wait
);
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/' */
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
)) {
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
);
418 put_prop_value_string_list(prop
, PR_PROTO_NAME
, get_protos(proto
));
420 if (!valid_props(prop
, NULL
, NULL
, NULL
, NULL
) || !valid
) {
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
));
428 free_instance_props(prop
);
433 valid_inetconfent(struct inetconfent
*iconf
, struct fileinfo
*finfo
)
435 boolean_t valid
= B_TRUE
;
441 char *proto
= iconf
->protocol
;
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
);
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
) {
455 iconf
->rpc_low_version
= strtol(cp
, &endp
, 10);
463 iconf
->rpc_high_version
= strtol(cp
,
465 if ((errno
!= 0) || (*endp
!= '\0'))
467 } else if (*cp
== '\0') {
468 iconf
->rpc_high_version
=
469 iconf
->rpc_low_version
;
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
);
481 proto
+= 4; /* skip 'rpc/' */
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
);
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
;
496 (void) fprintf(stderr
,
497 gettext("%s: Error %s line %d invalid wait-status: %s\n"),
498 progname
, finfo
->filename
, finfo
->lineno
,
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
,
511 if ((grp
= getgrgid(pwd
->pw_gid
)) != NULL
) {
512 iconf
->groupname
= safe_strdup(grp
->gr_name
);
514 /* use the group ID if no groupname */
517 len
= snprintf(s
, 1, "%d", pwd
->pw_gid
) + 1;
518 iconf
->groupname
= safe_malloc(len
);
519 (void) snprintf(iconf
->groupname
, len
, "%d",
524 /* check for internal services */
525 if (strcmp(iconf
->server_program
, "internal") == 0) {
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
);
537 (void) fprintf(stderr
, gettext("%s: Error %s line %d "
538 "unknown internal service: %s\n"), progname
,
539 finfo
->filename
, finfo
->lineno
, iconf
->service
);
541 } else if ((stat(iconf
->server_program
, &statb
) == -1) &&
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
);
550 return (valid
&& valid_basic_properties(iconf
, finfo
));
554 free_inetconfent(struct inetconfent
*iconf
)
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
);
574 static struct inetconfent
*
575 line_to_inetconfent(char *line
)
578 struct inetconfent
*iconf
;
580 iconf
= safe_malloc(sizeof (struct inetconfent
));
581 (void) memset(iconf
, 0, sizeof (struct inetconfent
));
583 if ((cp
= strtok(line
, " \t\n")) == NULL
)
585 iconf
->service
= safe_strdup(cp
);
587 if ((cp
= strtok(NULL
, " \t\n")) == NULL
)
589 iconf
->endpoint
= safe_strdup(cp
);
591 if ((cp
= strtok(NULL
, " \t\n")) == NULL
)
593 iconf
->protocol
= safe_strdup(cp
);
595 if ((cp
= strtok(NULL
, " \t\n")) == NULL
)
597 iconf
->wait_status
= safe_strdup(cp
);
599 if ((cp
= strtok(NULL
, " \t\n")) == NULL
)
601 iconf
->username
= safe_strdup(cp
);
603 if ((cp
= strtok(NULL
, " \t\n")) == NULL
)
605 iconf
->server_program
= safe_strdup(cp
);
607 /* last field is optional */
608 if ((cp
= strtok(NULL
, "\n")) != NULL
)
609 iconf
->server_args
= safe_strdup(cp
);
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
);
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
);
623 args
= safe_strdup(iconf
->server_args
);
624 if ((cp
= strtok(args
, " \t")) != NULL
) {
625 if ((endp
= strrchr(iconf
->exec
, '/')) == NULL
)
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
);
642 free_inetconfent(iconf
);
651 /* skip remainder of a line */
652 while (((c
= getc(fp
)) != EOF
) && (c
!= '\n'))
656 static struct inetconfent
*
657 fgetinetconfent(struct fileinfo
*finfo
, boolean_t validate
)
660 struct inetconfent
*iconf
;
661 char line
[MAX_SRC_LINELEN
];
663 while (fgets(line
, sizeof (line
), finfo
->fp
) != NULL
) {
666 /* skip empty or commented out lines */
670 if (line
[strlen(line
) - 1] != '\n')
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
);
683 /* remove in line comments and newline character */
684 if ((cp
= strchr(line
, '#')) == NULL
)
685 cp
= strchr(line
, '\n');
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
);
697 if (!validate
|| valid_inetconfent(iconf
, finfo
))
701 free_inetconfent(iconf
);
707 boolstr(boolean_t val
)
715 print_manifest(FILE *f
, char *filename
, struct inetconfent
*iconf
)
717 if (fprintf(f
, xml_header
) < 0)
720 if (fprintf(f
, xml_comment
,
721 iconf
->isrpc
? iconf
->rpc_prog
: iconf
->service
) < 0)
724 if (fprintf(f
, xml_service_bundle
, iconf
->service
) < 0)
726 if (fprintf(f
, xml_service_name
, servicename(iconf
)) < 0)
728 if (fprintf(f
, xml_instance
) < 0)
730 if (fprintf(f
, xml_restarter
, INETD_INSTANCE_FMRI
) < 0)
733 if (fprintf(f
, xml_dependency
, "rpcbind", RPCBIND_FMRI
) < 0)
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)
740 if (iconf
->arg0
!= NULL
) {
741 if (fprintf(f
, xml_arg0
, PR_ARG0_NAME
, iconf
->arg0
) < 0)
744 if (fprintf(f
, xml_exec_method_end
) < 0)
747 if (fprintf(f
, xml_exec_method_disable
, DISABLE_METHOD_NAME
,
750 if (fprintf(f
, xml_exec_method_end
) < 0)
754 if (fprintf(f
, xml_exec_method_offline
, OFFLINE_METHOD_NAME
,
757 if (fprintf(f
, xml_exec_method_end
) < 0)
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)
770 if (fprintf(f
, xml_property_group_end
) < 0)
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 :
778 PR_ISWAIT_NAME
, boolstr(iconf
->wait
),
779 PR_ISRPC_NAME
, boolstr(iconf
->isrpc
)) < 0)
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)
787 if (fprintf(f
, xml_property_group_end
) < 0)
790 if (fprintf(f
, xml_stability
) < 0)
792 if (fprintf(f
, xml_template
,
793 iconf
->isrpc
? iconf
->rpc_prog
: iconf
->service
) < 0)
795 if (fprintf(f
, xml_footer
) < 0)
798 (void) printf("%s -> %s\n", iconf
->service
, filename
);
802 (void) fprintf(stderr
, gettext("%s: Error writing manifest %s: %s\n"),
803 progname
, filename
, strerror(errno
));
807 static struct fileinfo
*
808 open_srcfile(char *filename
)
810 struct fileinfo
*finfo
= NULL
;
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
));
821 * If no source file specified, do the same as inetd and first
822 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
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
));
838 finfo
= safe_malloc(sizeof (struct fileinfo
));
840 finfo
->filename
= filename
;
843 (void) fcntl(fileno(fp
), F_SETFD
, FD_CLOEXEC
);
849 * Opens manifest output file. Returns 0 on success, -1 if the file
850 * exists, -2 on other errors.
856 struct inetconfent
*iconf
,
857 struct fileinfo
**finfo
)
861 char *dstfile
, *cp
, *proto
;
864 /* if no destdir specified, use appropriate default */
865 if (destdir
== NULL
) {
867 destdir
= MANIFEST_RPC_DIR
;
869 destdir
= MANIFEST_DIR
;
872 len
= strlen(destdir
) + strlen(iconf
->service
) +
873 strlen(iconf
->protocol
) + sizeof ("/-visible.xml");
874 dstfile
= safe_malloc(len
);
876 (void) strlcpy(dstfile
, destdir
, len
);
877 if (dstfile
[strlen(dstfile
) - 1] != '/')
878 (void) strlcat(dstfile
, "/", len
);
879 cp
= dstfile
+ strlen(dstfile
);
881 (void) strlcat(dstfile
, iconf
->service
, len
);
882 (void) strlcat(dstfile
, "-", len
);
884 proto
= iconf
->protocol
;
885 if (iconf
->isrpc
&& (strcmp(iconf
->protocol
, "rpc/*") == 0))
886 proto
= "rpc/visible";
888 (void) strlcat(dstfile
, proto
, len
);
889 (void) strlcat(dstfile
, ".xml", len
);
891 /* convert any '/' chars in service or protocol to '_' chars */
892 while ((cp
= strchr(cp
, '/')) != NULL
)
895 fd
= open(dstfile
, O_WRONLY
|O_CREAT
|(overwrite
? O_TRUNC
: O_EXCL
),
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
);
906 (void) fprintf(stderr
,
907 gettext("%s: Error opening %s: %s\n"),
908 progname
, dstfile
, strerror(errno
));
913 /* Clear errno to catch the "no stdio streams" case */
915 if ((fp
= fdopen(fd
, "w")) == NULL
) {
916 char *s
= strerror(errno
);
918 s
= gettext("No stdio streams available");
919 (void) fprintf(stderr
, gettext("%s: Error fdopen failed: %s\n"),
925 *finfo
= safe_malloc(sizeof (struct fileinfo
));
927 (*finfo
)->filename
= dstfile
;
928 (*finfo
)->lineno
= 0;
929 (*finfo
)->failcnt
= 0;
934 import_manifest(char *filename
)
940 if ((cp
= strrchr(filename
, '/')) == NULL
)
944 (void) printf(gettext("Importing %s ..."), cp
);
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
);
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
);
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
));
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
);
974 (void) printf(gettext("Done\n"));
979 inetd_config_path(char **path
)
982 char *arg1
, *configfile
, *configstr
;
983 scf_simple_prop_t
*sp
;
984 char cpath
[PATH_MAX
];
986 if ((sp
= scf_simple_prop_get(NULL
, INETD_INSTANCE_FMRI
, "start",
987 SCF_PROPERTY_EXEC
)) == NULL
)
989 if ((configstr
= scf_simple_prop_next_astring(sp
)) == NULL
) {
990 scf_simple_prop_free(sp
);
993 configstr
= safe_strdup(configstr
);
994 scf_simple_prop_free(sp
);
997 * Look for the optional configuration file, the syntax is:
998 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
1000 if (strtok(configstr
, " \t") == NULL
) {
1004 if ((arg1
= strtok(NULL
, " \t")) == NULL
) {
1008 if (strtok(NULL
, " \t") == NULL
) {
1010 * No configuration file specified, do the same as inetd and
1011 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
1013 configfile
= MAIN_CONFIG
;
1014 if ((fd
= open(configfile
, O_RDONLY
)) >= 0)
1017 configfile
= ALT_CONFIG
;
1020 /* make sure there are no more arguments */
1021 if (strtok(NULL
, " \t") != NULL
) {
1028 /* configuration file must be an absolute pathname */
1029 if (*configfile
!= '/') {
1034 if (realpath(configfile
, cpath
) == NULL
)
1035 (void) strlcpy(cpath
, configfile
, sizeof (cpath
));
1038 *path
= safe_strdup(cpath
);
1043 update_hash(char *srcfile
)
1046 char *inetd_cpath
, *hashstr
;
1047 char cpath
[PATH_MAX
];
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
);
1056 /* resolve inetconv input filename */
1057 if (realpath(srcfile
, cpath
) == NULL
)
1058 (void) strlcpy(cpath
, srcfile
, sizeof (cpath
));
1060 /* if inetconv and inetd are using the same config file, update hash */
1061 if (strcmp(cpath
, inetd_cpath
) != 0) {
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"),
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
));
1087 property_error(const char *fmri
, const char *prop
)
1089 (void) fprintf(stderr
,
1090 gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
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.
1099 * returns -1 on error, 1 on successful transaction completion.
1103 modify_sprop(scf_handle_t
*h
, const scf_instance_t
*inst
,
1104 const char *pg
, const char *prop
, const char *value
)
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;
1114 if ((gpg
= scf_pg_create(h
)) == NULL
)
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
) {
1124 if ((scf_instance_get_parent(inst
, svc
) == -1) ||
1125 (scf_service_get_pg(svc
, pg
, gpg
) == -1)) {
1131 if ((eprop
= scf_property_create(h
)) == NULL
) {
1136 if (scf_pg_get_property(gpg
, prop
, eprop
) == -1) {
1137 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
1145 if ((tx
= scf_transaction_create(h
)) == NULL
||
1146 (ent
= scf_entry_create(h
)) == NULL
) {
1152 if (scf_transaction_start(tx
, gpg
) == -1) {
1157 /* Modify the property */
1159 ret
= scf_transaction_property_new(tx
, ent
, prop
,
1162 ret
= scf_transaction_property_change_type(tx
, ent
,
1163 prop
, SCF_TYPE_ASTRING
);
1168 if ((v
= scf_value_create(h
)) == NULL
) {
1173 if (scf_value_set_astring(v
, value
) == -1) {
1178 if (scf_entry_add_value(ent
, v
) == -1) {
1183 ret
= scf_transaction_commit(tx
);
1186 /* Property group was stale, retry */
1187 if (scf_pg_update(gpg
) == -1) {
1191 scf_transaction_reset(tx
);
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
);
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.
1216 list_callback(scf_handle_t
*h
, scf_instance_t
*inst
, void *buf
)
1218 ssize_t max_name_length
;
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
;
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
);
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.
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."),
1245 return (SCF_FAILED
);
1248 if ((prop
= scf_simple_prop_get(h
, svc_name
, SCF_PG_GENERAL
,
1249 SCF_PROPERTY_RESTARTER
)) == NULL
)
1252 if ((restart_str
= scf_simple_prop_next_ustring(prop
)) == NULL
)
1255 if (strcmp(restart_str
, INETD_INSTANCE_FMRI
) != 0)
1258 /* Free restarter prop so it can be reused below */
1259 scf_simple_prop_free(prop
);
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.
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
);
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
);
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
);
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
);
1295 /* If it's RPC, we truncate off the version portion for comparison */
1299 cp
= strchr(iconf
->service
, '/');
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.
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 */
1322 gettext("Would update %s to %s %s"),
1323 svc_name
, PR_EXEC_NAME
, iconf
->exec
);
1324 if (iconf
->arg0
!= NULL
) {
1326 gettext(" with %s of %s\n"),
1327 PR_ARG0_NAME
, iconf
->arg0
);
1329 (void) printf("\n");
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()));
1341 (void) printf("%s will %s %s\n",
1342 svc_name
, PR_EXEC_NAME
,
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
,
1355 scf_strerror(scf_error()));
1357 (void) printf("%s will have an "
1358 "%s of %s\n", svc_name
,
1359 PR_ARG0_NAME
, iconf
->arg0
);
1367 (void) printf("Would enable %s\n", svc_name
);
1369 if (smf_enable_instance(svc_name
, 0) != 0)
1370 (void) fprintf(stderr
,
1371 gettext("Error: Failed to enable %s\n"),
1374 (void) printf("%s enabled\n", 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
);
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"
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"),
1406 main(int argc
, char *argv
[])
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
;
1416 setbuf(stdout
, NULL
);
1417 (void) setlocale(LC_ALL
, "");
1418 (void) textdomain(TEXT_DOMAIN
);
1420 if ((progname
= strrchr(argv
[0], '/')) == NULL
)
1425 while ((c
= getopt(argc
, argv
, "?efni:o:")) != -1) {
1428 /* enable services based on existing file config */
1433 /* overwrite existing manifests */
1437 /* don't import manifests, or dry-run enable */
1441 /* alternate input file */
1442 if (srcfile
!= NULL
) {
1443 (void) fprintf(stderr
,
1444 gettext("%s: Error only one -%c allowed\n"),
1451 /* alternate output directory */
1452 if (destdir
!= NULL
) {
1453 (void) fprintf(stderr
,
1454 gettext("%s: Error only one -%c allowed\n"),
1460 case '?': /*FALLTHROUGH*/
1468 * Display usage if extraneous args supplied or enable specified in
1469 * combination with overwrite or destdir
1471 if ((optind
!= argc
) || (enable
&& (overwrite
|| destdir
!= NULL
)))
1474 if ((srcfinfo
= open_srcfile(srcfile
)) == NULL
)
1475 return (EXIT_ERROR_CONV
);
1477 while ((iconf
= fgetinetconfent(srcfinfo
, !enable
)) != NULL
) {
1479 * If we're enabling, then just walk all the services for each
1480 * line and enable those which match.
1483 rval
= scf_simple_walk_instances(SCF_STATE_ALL
, iconf
,
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()));
1498 /* Remainder of loop used for conversion & import */
1499 if ((rval
= open_dstfile(destdir
, overwrite
, iconf
, &dstfinfo
))
1502 * Only increment error counter if the failure was
1503 * other than the file already existing.
1506 srcfinfo
->failcnt
++;
1507 free_inetconfent(iconf
);
1510 rval
= print_manifest(dstfinfo
->fp
, dstfinfo
->filename
, iconf
);
1511 (void) fclose(dstfinfo
->fp
);
1514 (import_manifest(dstfinfo
->filename
) != 0))
1517 (void) unlink(dstfinfo
->filename
);
1518 srcfinfo
->failcnt
++;
1520 free(dstfinfo
->filename
);
1522 free_inetconfent(iconf
);
1524 (void) fclose(srcfinfo
->fp
);
1525 convert_err
= srcfinfo
->failcnt
;
1527 /* Update hash only if not in enable mode, and only if importing */
1528 if (!enable
&& import
&& (update_hash(srcfinfo
->filename
) != 0))
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
);