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