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