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 2006 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 static boolean_t 374 valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo) 375 { 376 size_t prop_size; 377 inetd_prop_t *prop, *inetd_properties; 378 boolean_t valid = B_TRUE; 379 char *proto = iconf->protocol; 380 char *svc_name = iconf->service; 381 382 inetd_properties = get_prop_table(&prop_size); 383 prop = safe_malloc(prop_size * sizeof (inetd_prop_t)); 384 (void) memcpy(prop, inetd_properties, 385 prop_size * sizeof (inetd_prop_t)); 386 387 put_prop_value_boolean(prop, PR_ISRPC_NAME, iconf->isrpc); 388 put_prop_value_boolean(prop, PR_ISWAIT_NAME, iconf->wait); 389 if (iconf->isrpc) { 390 put_prop_value_int(prop, PR_RPC_LW_VER_NAME, 391 iconf->rpc_low_version); 392 put_prop_value_int(prop, PR_RPC_HI_VER_NAME, 393 iconf->rpc_high_version); 394 svc_name = iconf->rpc_prog; 395 proto += 4; /* skip 'rpc/' */ 396 } 397 398 if (!put_prop_value_string(prop, PR_SOCK_TYPE_NAME, iconf->endpoint) || 399 !put_prop_value_string(prop, PR_SVC_NAME_NAME, svc_name)) { 400 valid = B_FALSE; 401 402 if (errno == ENOMEM) { 403 (void) fprintf(stderr, 404 gettext("%s: failed to allocate memory: %s\n"), 405 progname, strerror(errno)); 406 exit(EXIT_ERROR_SYS); 407 } 408 } 409 410 put_prop_value_string_list(prop, PR_PROTO_NAME, get_protos(proto)); 411 412 if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) { 413 valid = B_FALSE; 414 (void) fprintf(stderr, gettext("%s: Error %s line %d " 415 "invalid or inconsistent fields:%s\n"), progname, 416 finfo->filename, finfo->lineno, 417 invalid_props(prop)); 418 } 419 420 free_instance_props(prop); 421 return (valid); 422 } 423 424 static boolean_t 425 valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo) 426 { 427 boolean_t valid = B_TRUE; 428 size_t len; 429 char *cp, *endp; 430 struct passwd *pwd; 431 struct group *grp; 432 struct stat statb; 433 char *proto = iconf->protocol; 434 435 iconf->isrpc = B_FALSE; 436 if (strncmp(iconf->protocol, "rpc/", 4) == 0) { 437 iconf->isrpc = B_TRUE; 438 iconf->rpc_prog = safe_strdup(iconf->service); 439 440 /* set RPC version numbers */ 441 iconf->rpc_low_version = 1; 442 iconf->rpc_high_version = 1; 443 if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) { 444 *cp = '\0'; 445 if (*++cp != '\0') { 446 errno = 0; 447 iconf->rpc_low_version = strtol(cp, &endp, 10); 448 if (errno != 0) 449 goto vererr; 450 cp = endp; 451 if (*cp == '-') { 452 if (*++cp == '\0') 453 goto vererr; 454 errno = 0; 455 iconf->rpc_high_version = strtol(cp, 456 &endp, 10); 457 if ((errno != 0) || (*endp != '\0')) 458 goto vererr; 459 } else if (*cp == '\0') { 460 iconf->rpc_high_version = 461 iconf->rpc_low_version; 462 } else { 463 vererr: 464 (void) fprintf(stderr, gettext( 465 "%s: Error %s line %d invalid RPC " 466 "version in service: %s\n"), 467 progname, finfo->filename, 468 finfo->lineno, iconf->service); 469 valid = B_FALSE; 470 } 471 } 472 } 473 proto += 4; /* skip 'rpc/' */ 474 } 475 /* tcp6only and udp6only are not valid in inetd.conf */ 476 if (is_v6only(proto)) { 477 (void) fprintf(stderr, gettext("%s: Error %s line %d " 478 "invalid protocol: %s\n"), progname, 479 finfo->filename, finfo->lineno, proto); 480 valid = B_FALSE; 481 } 482 483 if (strcmp(iconf->wait_status, "wait") == 0) { 484 iconf->wait = B_TRUE; 485 } else if (strcmp(iconf->wait_status, "nowait") == 0) { 486 iconf->wait = B_FALSE; 487 } else { 488 (void) fprintf(stderr, 489 gettext("%s: Error %s line %d invalid wait-status: %s\n"), 490 progname, finfo->filename, finfo->lineno, 491 iconf->wait_status); 492 valid = B_FALSE; 493 } 494 495 /* look up the username to set the groupname */ 496 if ((pwd = getpwnam(iconf->username)) == NULL) { 497 (void) fprintf(stderr, 498 gettext("%s: Error %s line %d unknown user: %s\n"), 499 progname, finfo->filename, finfo->lineno, 500 iconf->username); 501 valid = B_FALSE; 502 } else { 503 if ((grp = getgrgid(pwd->pw_gid)) != NULL) { 504 iconf->groupname = safe_strdup(grp->gr_name); 505 } else { 506 /* use the group ID if no groupname */ 507 char s[1]; 508 509 len = snprintf(s, 1, "%d", pwd->pw_gid) + 1; 510 iconf->groupname = safe_malloc(len); 511 (void) snprintf(iconf->groupname, len, "%d", 512 pwd->pw_gid); 513 } 514 } 515 516 /* check for internal services */ 517 if (strcmp(iconf->server_program, "internal") == 0) { 518 valid = B_FALSE; 519 if ((strcmp(iconf->service, "echo") == 0) || 520 (strcmp(iconf->service, "discard") == 0) || 521 (strcmp(iconf->service, "time") == 0) || 522 (strcmp(iconf->service, "daytime") == 0) || 523 (strcmp(iconf->service, "chargen") == 0)) { 524 (void) fprintf(stderr, gettext( 525 "%s: Error %s line %d the SUNWcnsr and SUNWcnsu" 526 " packages contain the internal services\n"), 527 progname, finfo->filename, finfo->lineno); 528 } else { 529 (void) fprintf(stderr, gettext("%s: Error %s line %d " 530 "unknown internal service: %s\n"), progname, 531 finfo->filename, finfo->lineno, iconf->service); 532 } 533 } else if ((stat(iconf->server_program, &statb) == -1) && 534 (errno == ENOENT)) { 535 (void) fprintf(stderr, gettext( 536 "%s: Error %s line %d server-program not found: %s\n"), 537 progname, finfo->filename, finfo->lineno, 538 iconf->server_program); 539 valid = B_FALSE; 540 } 541 542 return (valid && valid_basic_properties(iconf, finfo)); 543 } 544 545 static void 546 free_inetconfent(struct inetconfent *iconf) 547 { 548 if (iconf == NULL) 549 return; 550 551 free(iconf->service); 552 free(iconf->endpoint); 553 free(iconf->protocol); 554 free(iconf->wait_status); 555 free(iconf->username); 556 free(iconf->server_program); 557 free(iconf->server_args); 558 free(iconf->rpc_prog); 559 free(iconf->groupname); 560 free(iconf->exec); 561 free(iconf->arg0); 562 563 free(iconf); 564 } 565 566 static struct inetconfent * 567 line_to_inetconfent(char *line) 568 { 569 char *cp; 570 struct inetconfent *iconf; 571 572 iconf = safe_malloc(sizeof (struct inetconfent)); 573 (void) memset(iconf, 0, sizeof (struct inetconfent)); 574 575 if ((cp = strtok(line, " \t\n")) == NULL) 576 goto fail; 577 iconf->service = safe_strdup(cp); 578 579 if ((cp = strtok(NULL, " \t\n")) == NULL) 580 goto fail; 581 iconf->endpoint = safe_strdup(cp); 582 583 if ((cp = strtok(NULL, " \t\n")) == NULL) 584 goto fail; 585 iconf->protocol = safe_strdup(cp); 586 587 if ((cp = strtok(NULL, " \t\n")) == NULL) 588 goto fail; 589 iconf->wait_status = safe_strdup(cp); 590 591 if ((cp = strtok(NULL, " \t\n")) == NULL) 592 goto fail; 593 iconf->username = safe_strdup(cp); 594 595 if ((cp = strtok(NULL, " \t\n")) == NULL) 596 goto fail; 597 iconf->server_program = safe_strdup(cp); 598 599 /* last field is optional */ 600 if ((cp = strtok(NULL, "\n")) != NULL) 601 iconf->server_args = safe_strdup(cp); 602 603 /* Combine args and server name to construct exec and args fields */ 604 if (iconf->server_args == NULL) { 605 iconf->exec = safe_strdup(iconf->server_program); 606 } else { 607 int len; 608 char *args, *endp; 609 610 len = strlen(iconf->server_program) + 611 strlen(iconf->server_args) + 1; 612 iconf->exec = safe_malloc(len); 613 (void) strlcpy(iconf->exec, iconf->server_program, len); 614 615 args = safe_strdup(iconf->server_args); 616 if ((cp = strtok(args, " \t")) != NULL) { 617 if ((endp = strrchr(iconf->exec, '/')) == NULL) 618 endp = iconf->exec; 619 else 620 endp++; 621 /* only set arg0 property value if needed */ 622 if (strcmp(endp, cp) != 0) 623 iconf->arg0 = safe_strdup(cp); 624 while ((cp = strtok(NULL, " \t")) != NULL) { 625 (void) strlcat(iconf->exec, " ", len); 626 (void) strlcat(iconf->exec, cp, len); 627 } 628 } 629 free(args); 630 } 631 632 return (iconf); 633 fail: 634 free_inetconfent(iconf); 635 return (NULL); 636 } 637 638 static void 639 skipline(FILE *fp) 640 { 641 int c; 642 643 /* skip remainder of a line */ 644 while (((c = getc(fp)) != EOF) && (c != '\n')) 645 ; 646 } 647 648 static struct inetconfent * 649 fgetinetconfent(struct fileinfo *finfo, boolean_t validate) 650 { 651 char *cp; 652 struct inetconfent *iconf; 653 char line[MAX_SRC_LINELEN]; 654 655 while (fgets(line, sizeof (line), finfo->fp) != NULL) { 656 finfo->lineno++; 657 658 /* skip empty or commented out lines */ 659 if (*line == '\n') 660 continue; 661 if (*line == '#') { 662 if (line[strlen(line) - 1] != '\n') 663 skipline(finfo->fp); 664 continue; 665 } 666 /* check for lines which are too long */ 667 if (line[strlen(line) - 1] != '\n') { 668 (void) fprintf(stderr, 669 gettext("%s: Error %s line %d too long, skipped\n"), 670 progname, finfo->filename, finfo->lineno); 671 skipline(finfo->fp); 672 finfo->failcnt++; 673 continue; 674 } 675 /* remove in line comments and newline character */ 676 if ((cp = strchr(line, '#')) == NULL) 677 cp = strchr(line, '\n'); 678 if (cp) 679 *cp = '\0'; 680 681 if ((iconf = line_to_inetconfent(line)) == NULL) { 682 (void) fprintf(stderr, gettext( 683 "%s: Error %s line %d too few fields, skipped\n"), 684 progname, finfo->filename, finfo->lineno); 685 finfo->failcnt++; 686 continue; 687 } 688 689 if (!validate || valid_inetconfent(iconf, finfo)) 690 return (iconf); 691 692 finfo->failcnt++; 693 free_inetconfent(iconf); 694 } 695 return (NULL); 696 } 697 698 static char * 699 boolstr(boolean_t val) 700 { 701 if (val) 702 return ("true"); 703 return ("false"); 704 } 705 706 static int 707 print_manifest(FILE *f, char *filename, struct inetconfent *iconf) 708 { 709 if (fprintf(f, xml_header) < 0) 710 goto print_err; 711 712 if (fprintf(f, xml_comment, 713 iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0) 714 goto print_err; 715 716 if (fprintf(f, xml_service_bundle, iconf->service) < 0) 717 goto print_err; 718 if (fprintf(f, xml_service_name, servicename(iconf)) < 0) 719 goto print_err; 720 if (fprintf(f, xml_instance) < 0) 721 goto print_err; 722 if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0) 723 goto print_err; 724 if (iconf->isrpc) { 725 if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_FMRI) < 0) 726 goto print_err; 727 } 728 729 if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME, 730 iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0) 731 goto print_err; 732 if (iconf->arg0 != NULL) { 733 if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0) 734 goto print_err; 735 } 736 if (fprintf(f, xml_exec_method_end) < 0) 737 goto print_err; 738 739 if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME, 740 PR_EXEC_NAME) < 0) 741 goto print_err; 742 if (fprintf(f, xml_exec_method_end) < 0) 743 goto print_err; 744 745 if (iconf->wait) { 746 if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME, 747 PR_EXEC_NAME) < 0) 748 goto print_err; 749 if (fprintf(f, xml_exec_method_end) < 0) 750 goto print_err; 751 } 752 753 if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV, 754 PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE), 755 PR_VERSION_NAME, INETCONV_VERSION, 756 PR_SOURCE_LINE_NAME, iconf->service, 757 iconf->endpoint, iconf->protocol, iconf->wait_status, 758 iconf->username, iconf->server_program, 759 iconf->server_args == NULL ? "" : " ", 760 iconf->server_args == NULL ? "" : iconf->server_args) < 0) 761 goto print_err; 762 if (fprintf(f, xml_property_group_end) < 0) 763 goto print_err; 764 765 if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG, 766 PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service, 767 PR_SOCK_TYPE_NAME, iconf->endpoint, 768 PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 : 769 iconf->protocol, 770 PR_ISWAIT_NAME, boolstr(iconf->wait), 771 PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0) 772 goto print_err; 773 if (iconf->isrpc) { 774 if (fprintf(f, xml_property_group_rpc, 775 PR_RPC_LW_VER_NAME, iconf->rpc_low_version, 776 PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0) 777 goto print_err; 778 } 779 if (fprintf(f, xml_property_group_end) < 0) 780 goto print_err; 781 782 if (fprintf(f, xml_stability) < 0) 783 goto print_err; 784 if (fprintf(f, xml_template, 785 iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0) 786 goto print_err; 787 if (fprintf(f, xml_footer) < 0) 788 goto print_err; 789 790 (void) printf("%s -> %s\n", iconf->service, filename); 791 return (0); 792 793 print_err: 794 (void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"), 795 progname, filename, strerror(errno)); 796 return (-1); 797 } 798 799 static struct fileinfo * 800 open_srcfile(char *filename) 801 { 802 struct fileinfo *finfo = NULL; 803 FILE *fp; 804 805 if (filename != NULL) { 806 if ((fp = fopen(filename, "r")) == NULL) { 807 (void) fprintf(stderr, 808 gettext("%s: Error opening %s: %s\n"), 809 progname, filename, strerror(errno)); 810 } 811 } else { 812 /* 813 * If no source file specified, do the same as inetd and first 814 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf. 815 */ 816 filename = MAIN_CONFIG; 817 if ((fp = fopen(filename, "r")) == NULL) { 818 (void) fprintf(stderr, 819 gettext("%s: Error opening %s: %s\n"), 820 progname, filename, strerror(errno)); 821 filename = ALT_CONFIG; 822 if ((fp = fopen(filename, "r")) == NULL) { 823 (void) fprintf(stderr, gettext( 824 "%s: Error opening %s: %s\n"), progname, 825 filename, strerror(errno)); 826 } 827 } 828 } 829 if (fp != NULL) { 830 finfo = safe_malloc(sizeof (struct fileinfo)); 831 finfo->fp = fp; 832 finfo->filename = filename; 833 finfo->lineno = 0; 834 finfo->failcnt = 0; 835 (void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); 836 } 837 return (finfo); 838 } 839 840 /* 841 * Opens manifest output file. Returns 0 on success, -1 if the file 842 * exists, -2 on other errors. 843 */ 844 static int 845 open_dstfile( 846 char *destdir, 847 boolean_t overwrite, 848 struct inetconfent *iconf, 849 struct fileinfo **finfo) 850 { 851 int fd; 852 size_t len; 853 char *dstfile, *cp, *proto; 854 FILE *fp; 855 856 /* if no destdir specified, use appropriate default */ 857 if (destdir == NULL) { 858 if (iconf->isrpc) 859 destdir = MANIFEST_RPC_DIR; 860 else 861 destdir = MANIFEST_DIR; 862 } 863 864 len = strlen(destdir) + strlen(iconf->service) + 865 strlen(iconf->protocol) + sizeof ("/-visible.xml"); 866 dstfile = safe_malloc(len); 867 868 (void) strlcpy(dstfile, destdir, len); 869 if (dstfile[strlen(dstfile) - 1] != '/') 870 (void) strlcat(dstfile, "/", len); 871 cp = dstfile + strlen(dstfile); 872 873 (void) strlcat(dstfile, iconf->service, len); 874 (void) strlcat(dstfile, "-", len); 875 876 proto = iconf->protocol; 877 if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0)) 878 proto = "rpc/visible"; 879 880 (void) strlcat(dstfile, proto, len); 881 (void) strlcat(dstfile, ".xml", len); 882 883 /* convert any '/' chars in service or protocol to '_' chars */ 884 while ((cp = strchr(cp, '/')) != NULL) 885 *cp = '_'; 886 887 fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL), 888 0644); 889 if (fd == -1) { 890 if (!overwrite && (errno == EEXIST)) { 891 (void) fprintf(stderr, 892 gettext("%s: Notice: Service manifest for " 893 "%s already generated as %s, skipped\n"), 894 progname, iconf->service, dstfile); 895 free(dstfile); 896 return (-1); 897 } else { 898 (void) fprintf(stderr, 899 gettext("%s: Error opening %s: %s\n"), 900 progname, dstfile, strerror(errno)); 901 free(dstfile); 902 return (-2); 903 } 904 } 905 /* Clear errno to catch the "no stdio streams" case */ 906 errno = 0; 907 if ((fp = fdopen(fd, "w")) == NULL) { 908 char *s = strerror(errno); 909 if (errno == 0) 910 s = gettext("No stdio streams available"); 911 (void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"), 912 progname, s); 913 (void) close(fd); 914 free(dstfile); 915 return (-2); 916 } 917 *finfo = safe_malloc(sizeof (struct fileinfo)); 918 (*finfo)->fp = fp; 919 (*finfo)->filename = dstfile; 920 (*finfo)->lineno = 0; 921 (*finfo)->failcnt = 0; 922 return (0); 923 } 924 925 static int 926 import_manifest(char *filename) 927 { 928 int status; 929 pid_t pid, wpid; 930 char *cp; 931 932 if ((cp = strrchr(filename, '/')) == NULL) 933 cp = filename; 934 else 935 cp++; 936 (void) printf(gettext("Importing %s ..."), cp); 937 938 if ((pid = fork()) == -1) { 939 (void) fprintf(stderr, 940 gettext("\n%s: fork failed, %s not imported: %s\n"), 941 progname, filename, strerror(errno)); 942 exit(EXIT_ERROR_SYS); 943 } 944 if (pid == 0) { 945 /* child */ 946 (void) fclose(stdin); 947 (void) setenv("SVCCFG_CHECKHASH", "1", 1); 948 (void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL); 949 (void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"), 950 progname, SVCCFG_PATH, strerror(errno)); 951 _exit(EXIT_ERROR_SYS); 952 } 953 /* parent */ 954 if ((wpid = waitpid(pid, &status, 0)) != pid) { 955 (void) fprintf(stderr, gettext( 956 "\n%s: unexpected wait (%d) from import of %s: %s\n"), 957 progname, wpid, filename, strerror(errno)); 958 return (-1); 959 } 960 if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) { 961 (void) fprintf(stderr, 962 gettext("\n%s: import failure (%d) for %s\n"), 963 progname, WEXITSTATUS(status), filename); 964 return (-1); 965 } 966 (void) printf(gettext("Done\n")); 967 return (0); 968 } 969 970 static int 971 inetd_config_path(char **path) 972 { 973 int fd; 974 char *arg1, *configfile, *configstr; 975 scf_simple_prop_t *sp; 976 char cpath[PATH_MAX]; 977 978 if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start", 979 SCF_PROPERTY_EXEC)) == NULL) 980 return (-1); 981 if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) { 982 scf_simple_prop_free(sp); 983 return (-1); 984 } 985 configstr = safe_strdup(configstr); 986 scf_simple_prop_free(sp); 987 988 /* 989 * Look for the optional configuration file, the syntax is: 990 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m 991 */ 992 if (strtok(configstr, " \t") == NULL) { 993 free(configstr); 994 return (-1); 995 } 996 if ((arg1 = strtok(NULL, " \t")) == NULL) { 997 free(configstr); 998 return (-1); 999 } 1000 if (strtok(NULL, " \t") == NULL) { 1001 /* 1002 * No configuration file specified, do the same as inetd and 1003 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf. 1004 */ 1005 configfile = MAIN_CONFIG; 1006 if ((fd = open(configfile, O_RDONLY)) >= 0) 1007 (void) close(fd); 1008 else 1009 configfile = ALT_CONFIG; 1010 1011 } else { 1012 /* make sure there are no more arguments */ 1013 if (strtok(NULL, " \t") != NULL) { 1014 free(configstr); 1015 return (-1); 1016 } 1017 configfile = arg1; 1018 } 1019 1020 /* configuration file must be an absolute pathname */ 1021 if (*configfile != '/') { 1022 free(configstr); 1023 return (-1); 1024 } 1025 1026 if (realpath(configfile, cpath) == NULL) 1027 (void) strlcpy(cpath, configfile, sizeof (cpath)); 1028 1029 free(configstr); 1030 *path = safe_strdup(cpath); 1031 return (0); 1032 } 1033 1034 static int 1035 update_hash(char *srcfile) 1036 { 1037 scf_error_t rval; 1038 char *inetd_cpath, *hashstr; 1039 char cpath[PATH_MAX]; 1040 1041 /* determine the config file inetd is using */ 1042 if (inetd_config_path(&inetd_cpath) == -1) { 1043 (void) fprintf(stderr, 1044 gettext("%s: Error reading from repository\n"), progname); 1045 return (-1); 1046 } 1047 1048 /* resolve inetconv input filename */ 1049 if (realpath(srcfile, cpath) == NULL) 1050 (void) strlcpy(cpath, srcfile, sizeof (cpath)); 1051 1052 /* if inetconv and inetd are using the same config file, update hash */ 1053 if (strcmp(cpath, inetd_cpath) != 0) { 1054 free(inetd_cpath); 1055 return (0); 1056 } 1057 free(inetd_cpath); 1058 1059 /* generic error message as use of hash is not exposed to the user */ 1060 if (calculate_hash(cpath, &hashstr) != 0) { 1061 (void) fprintf(stderr, 1062 gettext("%s: Error unable to update repository\n"), 1063 progname); 1064 return (-1); 1065 } 1066 /* generic error message as use of hash is not exposed to the user */ 1067 if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) { 1068 (void) fprintf(stderr, 1069 gettext("%s: Error updating repository: %s\n"), 1070 progname, scf_strerror(rval)); 1071 free(hashstr); 1072 return (-1); 1073 } 1074 free(hashstr); 1075 return (0); 1076 } 1077 1078 static void 1079 property_error(const char *fmri, const char *prop) 1080 { 1081 (void) fprintf(stderr, 1082 gettext("Error: Instance %1$s is missing property '%2$s'.\n"), 1083 fmri, prop); 1084 } 1085 1086 /* 1087 * modify_sprop takes a handle, an instance, a property group, a property, 1088 * and an astring value, and modifies the instance (or service's) specified 1089 * property in the repository to the submitted value. 1090 * 1091 * returns -1 on error, 1 on successful transaction completion. 1092 */ 1093 1094 static int 1095 modify_sprop(scf_handle_t *h, const scf_instance_t *inst, 1096 const char *pg, const char *prop, const char *value) 1097 { 1098 scf_transaction_t *tx = NULL; 1099 scf_transaction_entry_t *ent = NULL; 1100 scf_propertygroup_t *gpg = NULL; 1101 scf_property_t *eprop = NULL; 1102 scf_value_t *v = NULL; 1103 scf_service_t *svc = NULL; 1104 int ret = 0, create = 0; 1105 1106 if ((gpg = scf_pg_create(h)) == NULL) 1107 return (-1); 1108 1109 /* Get the property group */ 1110 if (scf_instance_get_pg(inst, pg, gpg) == -1) { 1111 /* Not a property of the instance, try the service instead */ 1112 if ((svc = scf_service_create(h)) == NULL) { 1113 ret = -1; 1114 goto out; 1115 } 1116 if ((scf_instance_get_parent(inst, svc) == -1) || 1117 (scf_service_get_pg(svc, pg, gpg) == -1)) { 1118 ret = -1; 1119 goto out; 1120 } 1121 } 1122 1123 if ((eprop = scf_property_create(h)) == NULL) { 1124 ret = -1; 1125 goto out; 1126 } 1127 1128 if (scf_pg_get_property(gpg, prop, eprop) == -1) { 1129 if (scf_error() != SCF_ERROR_NOT_FOUND) { 1130 ret = -1; 1131 goto out; 1132 } 1133 1134 create = 1; 1135 } 1136 1137 if ((tx = scf_transaction_create(h)) == NULL || 1138 (ent = scf_entry_create(h)) == NULL) { 1139 ret = -1; 1140 goto out; 1141 } 1142 1143 do { 1144 if (scf_transaction_start(tx, gpg) == -1) { 1145 ret = -1; 1146 goto out; 1147 } 1148 1149 /* Modify the property */ 1150 if (create) 1151 ret = scf_transaction_property_new(tx, ent, prop, 1152 SCF_TYPE_ASTRING); 1153 else 1154 ret = scf_transaction_property_change_type(tx, ent, 1155 prop, SCF_TYPE_ASTRING); 1156 1157 if (ret == -1) 1158 goto out; 1159 1160 if ((v = scf_value_create(h)) == NULL) { 1161 ret = -1; 1162 goto out; 1163 } 1164 1165 if (scf_value_set_astring(v, value) == -1) { 1166 ret = -1; 1167 goto out; 1168 } 1169 1170 if (scf_entry_add_value(ent, v) == -1) { 1171 ret = -1; 1172 goto out; 1173 } 1174 1175 ret = scf_transaction_commit(tx); 1176 1177 if (ret == 0) { 1178 /* Property group was stale, retry */ 1179 if (scf_pg_update(gpg) == -1) { 1180 ret = -1; 1181 goto out; 1182 } 1183 scf_transaction_reset(tx); 1184 } 1185 1186 } while (ret == 0); 1187 out: 1188 scf_value_destroy(v); 1189 scf_entry_destroy(ent); 1190 scf_transaction_destroy(tx); 1191 scf_property_destroy(eprop); 1192 scf_service_destroy(svc); 1193 scf_pg_destroy(gpg); 1194 1195 return (ret); 1196 } 1197 1198 /* 1199 * list_callback is the callback function to be handed to simple_walk_instances 1200 * in main. It is called once on every instance on a machine. If that 1201 * instance is controlled by inetd, we test whether it's the same 1202 * service that we're looking at from the inetd.conf file, and enable it if 1203 * they are the same. 1204 */ 1205 1206 /*ARGSUSED*/ 1207 static int 1208 list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf) 1209 { 1210 ssize_t max_name_length; 1211 char *svc_name; 1212 scf_simple_prop_t *prop = NULL; 1213 scf_simple_prop_t *sockprop = NULL; 1214 scf_simple_prop_t *rpcprop = NULL; 1215 scf_simple_prop_t *progprop = NULL; 1216 const char *name, *endpoint, *restart_str, *prog; 1217 struct inetconfent *iconf = (struct inetconfent *)buf; 1218 uint8_t *isrpc; 1219 1220 max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 1221 if ((svc_name = malloc(max_name_length + 1)) == NULL) { 1222 (void) fprintf(stderr, gettext("Error: Out of memory.\n")); 1223 return (SCF_FAILED); 1224 } 1225 1226 /* 1227 * Get the FMRI of the instance, and check if its delegated restarter 1228 * is inetd. A missing or empty restarter property implies that 1229 * svc.startd is the restarter. 1230 */ 1231 1232 if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) { 1233 (void) fprintf(stderr, 1234 gettext("Error: Unable to obtain FMRI for service %1$s."), 1235 svc_name); 1236 free(svc_name); 1237 return (SCF_FAILED); 1238 } 1239 1240 if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL, 1241 SCF_PROPERTY_RESTARTER)) == NULL) 1242 goto out; 1243 1244 if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL) 1245 goto out; 1246 1247 if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0) 1248 goto out; 1249 1250 /* Free restarter prop so it can be reused below */ 1251 scf_simple_prop_free(prop); 1252 1253 /* 1254 * We know that this instance is managed by inetd. 1255 * Now get the properties needed to decide if it matches this 1256 * line in the old config file. 1257 */ 1258 1259 if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG, 1260 PR_SVC_NAME_NAME)) == NULL) || 1261 ((name = scf_simple_prop_next_astring(prop)) == NULL)) { 1262 property_error(svc_name, PR_SVC_NAME_NAME); 1263 goto out; 1264 } 1265 1266 if (((sockprop = scf_simple_prop_get(h, svc_name, 1267 PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) || 1268 ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) { 1269 property_error(svc_name, PR_SOCK_TYPE_NAME); 1270 goto out; 1271 } 1272 1273 if (((rpcprop = scf_simple_prop_get(h, svc_name, 1274 PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) || 1275 ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) { 1276 property_error(svc_name, PR_ISRPC_NAME); 1277 goto out; 1278 } 1279 1280 if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME, 1281 PR_EXEC_NAME)) == NULL) || 1282 ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) { 1283 property_error(svc_name, PR_EXEC_NAME); 1284 } 1285 1286 1287 /* If it's RPC, we truncate off the version portion for comparison */ 1288 if (*isrpc) { 1289 char *cp; 1290 1291 cp = strchr(iconf->service, '/'); 1292 if (cp != NULL) 1293 *cp = '\0'; 1294 } 1295 1296 /* 1297 * If name of this service and endpoint are equal to values from 1298 * iconf fields, and they're either both RPC or both non-RPC, 1299 * then we have a match; update the exec and arg0 properties if 1300 * necessary, then enable it. 1301 * We don't return an error if either operation fails so that we 1302 * continue to try all the other services. 1303 */ 1304 if (strcmp(name, iconf->service) == 0 && 1305 strcmp(endpoint, iconf->endpoint) == 0 && 1306 *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) { 1307 /* Can't update exec on internal services */ 1308 if ((strcmp(iconf->server_program, "internal") != 0) && 1309 (strcmp(iconf->exec, prog) != 0)) { 1310 /* User had edited the command */ 1311 if (!import) { 1312 /* Dry run only */ 1313 (void) printf( 1314 gettext("Would update %s to %s %s"), 1315 svc_name, PR_EXEC_NAME, iconf->exec); 1316 if (iconf->arg0 != NULL) { 1317 (void) printf( 1318 gettext(" with %s of %s\n"), 1319 PR_ARG0_NAME, iconf->arg0); 1320 } else { 1321 (void) printf("\n"); 1322 } 1323 } else { 1324 /* Update instance's exec property */ 1325 if (modify_sprop(h, inst, START_METHOD_NAME, 1326 PR_EXEC_NAME, iconf->exec) != 1) 1327 (void) fprintf(stderr, 1328 gettext("Error: Unable to update " 1329 "%s property of %s, %s\n"), 1330 PR_EXEC_NAME, svc_name, 1331 scf_strerror(scf_error())); 1332 else 1333 (void) printf("%s will %s %s\n", 1334 svc_name, PR_EXEC_NAME, 1335 iconf->exec); 1336 1337 /* Update arg0 prop, if needed */ 1338 if (iconf->arg0 != NULL) { 1339 if (modify_sprop(h, inst, 1340 START_METHOD_NAME, PR_ARG0_NAME, 1341 iconf->arg0) != 1) { 1342 (void) fprintf(stderr, 1343 gettext("Error: Unable to " 1344 "update %s property of " 1345 "%s, %s\n"), PR_ARG0_NAME, 1346 svc_name, 1347 scf_strerror(scf_error())); 1348 } else { 1349 (void) printf("%s will have an " 1350 "%s of %s\n", svc_name, 1351 PR_ARG0_NAME, iconf->arg0); 1352 } 1353 } 1354 } 1355 } 1356 1357 if (!import) { 1358 /* Dry-run only */ 1359 (void) printf("Would enable %s\n", svc_name); 1360 } else { 1361 if (smf_enable_instance(svc_name, 0) != 0) 1362 (void) fprintf(stderr, 1363 gettext("Error: Failed to enable %s\n"), 1364 svc_name); 1365 else 1366 (void) printf("%s enabled\n", svc_name); 1367 } 1368 } 1369 1370 out: 1371 free(svc_name); 1372 scf_simple_prop_free(prop); 1373 scf_simple_prop_free(sockprop); 1374 scf_simple_prop_free(rpcprop); 1375 scf_simple_prop_free(progprop); 1376 return (SCF_SUCCESS); 1377 } 1378 1379 static void 1380 usage(void) 1381 { 1382 (void) fprintf(stderr, gettext( 1383 "Usage: %s [-fn] [-i srcfile] [-o destdir]\n" 1384 " %1$s -e [-n] [-i srcfile]\n" 1385 "-? Display this usage message\n" 1386 "-e Enable smf services which are enabled in the input\n" 1387 " file\n" 1388 "-f Force overwrite of existing manifests\n" 1389 "-n Do not import converted manifests,\n" 1390 " or only display services which would be enabled\n" 1391 "-i srcfile Alternate input file\n" 1392 "-o destdir Alternate output directory for manifests\n"), 1393 progname); 1394 exit(EXIT_USAGE); 1395 } 1396 1397 int 1398 main(int argc, char *argv[]) 1399 { 1400 int c, rval, convert_err, import_err = 0, enable_err = 0; 1401 boolean_t overwrite = B_FALSE; 1402 boolean_t enable = B_FALSE; 1403 char *srcfile = NULL; 1404 char *destdir = NULL; 1405 struct fileinfo *srcfinfo, *dstfinfo; 1406 struct inetconfent *iconf; 1407 1408 setbuf(stdout, NULL); 1409 (void) setlocale(LC_ALL, ""); 1410 (void) textdomain(TEXT_DOMAIN); 1411 1412 if ((progname = strrchr(argv[0], '/')) == NULL) 1413 progname = argv[0]; 1414 else 1415 progname++; 1416 1417 while ((c = getopt(argc, argv, "?efni:o:")) != -1) { 1418 switch (c) { 1419 case 'e': 1420 /* enable services based on existing file config */ 1421 enable = B_TRUE; 1422 break; 1423 1424 case 'f': 1425 /* overwrite existing manifests */ 1426 overwrite = B_TRUE; 1427 break; 1428 case 'n': 1429 /* don't import manifests, or dry-run enable */ 1430 import = B_FALSE; 1431 break; 1432 case 'i': 1433 /* alternate input file */ 1434 if (srcfile != NULL) { 1435 (void) fprintf(stderr, 1436 gettext("%s: Error only one -%c allowed\n"), 1437 progname, optopt); 1438 usage(); 1439 } 1440 srcfile = optarg; 1441 break; 1442 case 'o': 1443 /* alternate output directory */ 1444 if (destdir != NULL) { 1445 (void) fprintf(stderr, 1446 gettext("%s: Error only one -%c allowed\n"), 1447 progname, optopt); 1448 usage(); 1449 } 1450 destdir = optarg; 1451 break; 1452 case '?': /*FALLTHROUGH*/ 1453 default: 1454 usage(); 1455 break; 1456 } 1457 } 1458 1459 /* 1460 * Display usage if extraneous args supplied or enable specified in 1461 * combination with overwrite or destdir 1462 */ 1463 if ((optind != argc) || (enable && (overwrite || destdir != NULL))) 1464 usage(); 1465 1466 if ((srcfinfo = open_srcfile(srcfile)) == NULL) 1467 return (EXIT_ERROR_CONV); 1468 1469 while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) { 1470 /* 1471 * If we're enabling, then just walk all the services for each 1472 * line and enable those which match. 1473 */ 1474 if (enable) { 1475 rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf, 1476 list_callback); 1477 free_inetconfent(iconf); 1478 if (rval == SCF_FAILED) { 1479 /* Only print msg if framework error */ 1480 if (scf_error() != SCF_ERROR_CALLBACK_FAILED) 1481 (void) fprintf(stderr, gettext( 1482 "Error walking instances: %s.\n"), 1483 scf_strerror(scf_error())); 1484 enable_err++; 1485 break; 1486 } 1487 continue; 1488 } 1489 1490 /* Remainder of loop used for conversion & import */ 1491 if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo)) 1492 < 0) { 1493 /* 1494 * Only increment error counter if the failure was 1495 * other than the file already existing. 1496 */ 1497 if (rval == -2) 1498 srcfinfo->failcnt++; 1499 free_inetconfent(iconf); 1500 continue; 1501 } 1502 rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf); 1503 (void) fclose(dstfinfo->fp); 1504 if (rval == 0) { 1505 if (import && 1506 (import_manifest(dstfinfo->filename) != 0)) 1507 import_err++; 1508 } else { 1509 (void) unlink(dstfinfo->filename); 1510 srcfinfo->failcnt++; 1511 } 1512 free(dstfinfo->filename); 1513 free(dstfinfo); 1514 free_inetconfent(iconf); 1515 } 1516 (void) fclose(srcfinfo->fp); 1517 convert_err = srcfinfo->failcnt; 1518 1519 /* Update hash only if not in enable mode, and only if importing */ 1520 if (!enable && import && (update_hash(srcfinfo->filename) != 0)) 1521 import_err++; 1522 1523 free(srcfinfo); 1524 1525 if (enable_err != 0) 1526 return (EXIT_ERROR_ENBL); 1527 if (import_err != 0) 1528 return (EXIT_ERROR_IMP); 1529 if (convert_err != 0) 1530 return (EXIT_ERROR_CONV); 1531 return (EXIT_SUCCESS); 1532 } 1533