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 77struct 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 97struct fileinfo { 98 FILE *fp; 99 char *filename; 100 int lineno; 101 int failcnt; 102}; 103 104static char *progname; 105 106static boolean_t import = B_TRUE; 107 108/* start of manifest XML template strings */ 109static 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 114static 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 121static const char xml_service_bundle[] = 122"<service_bundle type='manifest' name='inetconv:%s'>\n\n"; 123 124static const char xml_service_name[] = 125"<service\n" 126" name='network/%s'\n" 127" type='service'\n" 128" version='1'>\n\n"; 129 130static 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 139static const char xml_instance[] = 140" <create_default_instance enabled='true'/>\n\n"; 141 142static const char xml_restarter[] = 143" <restarter>\n" 144" <service_fmri value='%s' />\n" 145" </restarter>\n\n"; 146 147static 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 165static const char xml_arg0[] = 166" <propval name='%s' type='astring'\n" 167" value='%s' />\n"; 168 169static const char xml_exec_method_end[] = 170" </exec_method>\n\n"; 171 172static 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 182static 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 193static 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 206static 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 214static 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 219static const char xml_property_group_end[] = 220" </property_group>\n\n"; 221 222static const char xml_stability[] = 223" <stability value='External' />\n\n"; 224 225static 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 234static const char xml_footer[] = 235"</service>\n" 236"\n" 237"</service_bundle>\n"; 238/* end of manifest XML template strings */ 239 240static void * 241safe_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 253static char * 254safe_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 266static char * 267propertyname(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 306static char * 307servicename(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 347static boolean_t 348is_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 357static char * 358invalid_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 381static boolean_t 382valid_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 432static boolean_t 433valid_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 { 471vererr: 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 553static void 554free_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 574static struct inetconfent * 575line_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); 641fail: 642 free_inetconfent(iconf); 643 return (NULL); 644} 645 646static void 647skipline(FILE *fp) 648{ 649 int c; 650 651 /* skip remainder of a line */ 652 while (((c = getc(fp)) != EOF) && (c != '\n')) 653 ; 654} 655 656static struct inetconfent * 657fgetinetconfent(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 706static char * 707boolstr(boolean_t val) 708{ 709 if (val) 710 return ("true"); 711 return ("false"); 712} 713 714static int 715print_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 801print_err: 802 (void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"), 803 progname, filename, strerror(errno)); 804 return (-1); 805} 806 807static struct fileinfo * 808open_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 */ 852static int 853open_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 933static int 934import_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 978static int 979inetd_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 1042static int 1043update_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 1086static void 1087property_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 1102static int 1103modify_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); 1195out: 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*/ 1215static int 1216list_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 1378out: 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 1387static void 1388usage(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 1405int 1406main(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