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