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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <locale.h> 31 #include <strings.h> 32 #include <errno.h> 33 #include <limits.h> 34 #include "idmap_engine.h" 35 #include "idmap_priv.h" 36 37 /* Initialization values for pids/rids: */ 38 39 #define UNDEFINED_UID (uid_t)-1 40 #define UNDEFINED_GID (gid_t)-1 41 #define UNDEFINED_RID (idmap_rid_t)-1; 42 43 /* is_user values */ 44 45 #define I_YES 1 46 #define I_NO 0 47 #define I_UNKNOWN -1 48 49 /* Directions */ 50 51 #define DIR_W2U 1 52 #define DIR_U2W 2 53 #define DIR_BI 0 54 #define DIR_UNKNOWN -1 55 56 /* 57 * used in do_show for the type of argument, which can be winname, 58 * unixname, uid, gid, sid or not given at all: 59 */ 60 61 #define TYPE_SID 0x010 /* sid */ 62 #define TYPE_WN 0x110 /* winname */ 63 #define TYPE_UID 0x001 /* uid */ 64 #define TYPE_GID 0x002 /* gid */ 65 #define TYPE_PID 0x000 /* pid */ 66 #define TYPE_UN 0x100 /* unixname */ 67 68 #define IS_WIN 0x010 /* mask for the windows types */ 69 #define IS_NAME 0x100 /* mask for string name types */ 70 #define IS_GROUP 0x002 /* mask for, well, TYPE_GID */ 71 72 73 /* Identity type strings */ 74 75 #define ID_WINNAME "winname" 76 #define ID_UNIXNAME "unixname" 77 #define ID_SID "sid" 78 #define ID_UID "uid" 79 #define ID_GID "gid" 80 81 /* Flags */ 82 83 #define g_FLAG 'g' 84 #define u_FLAG 'u' 85 #define f_FLAG 'f' 86 #define t_FLAG 't' 87 #define d_FLAG 'd' 88 #define F_FLAG 'F' 89 #define a_FLAG 'a' 90 #define n_FLAG 'n' 91 #define c_FLAG 'c' 92 93 94 /* used in the function do_import */ 95 #define MAX_INPUT_LINE_SZ 2047 96 97 98 typedef struct { 99 int is_user; 100 int direction; 101 boolean_t is_nt4; 102 char *unixname; 103 char *winname; 104 char *windomain; 105 char *sidprefix; 106 idmap_rid_t rid; 107 uid_t pid; 108 } name_mapping_t; 109 110 /* 111 * Formats of the output: 112 * 113 * Idmap reads/prints mappings in several formats: ordinary mappings, 114 * name mappings in Samba username map format (smbusers), Netapp 115 * usermap.cfg. 116 * 117 * DEFAULT_FORMAT are in fact the idmap subcommands suitable for 118 * piping to idmap standart input. For example 119 * add -u -d winname:bob@foo.com unixname:fred 120 * add -u -d winname:bob2bar.com unixname:fred 121 * 122 * SMBUSERS is the format of Samba username map (smbusers). For full 123 * documentation, search for "username map" in smb.conf manpage. 124 * The format is for example 125 * fred = bob@foo.com bob2@bar.com 126 * 127 * USERMAP_CFG is the format of Netapp usermap.cfg file. Search 128 * http://www.netapp.com/ for more documentation. IP qualifiers are not 129 * supported. 130 * The format is for example 131 * bob@foo.com => fred 132 * "Bob With Spaces"@bar.com => fred #comment 133 * 134 * The previous formats were for name rules. MAPPING_NAME and 135 * MAPPING_ID are for the actual mappings, as seen in show/dump 136 * commands. MAPPING_NAME prefers the string names of the user over 137 * their numerical identificators. MAPPING_ID prints just the 138 * identificators. 139 * Example of the MAPPING_NAME: 140 * winname:bob@foo.com -> unixname:fred 141 * 142 * Example of the MAPPING_ID: 143 * sid:S-1-2-3-4 -> uid:5678 144 */ 145 146 typedef enum { 147 UNDEFINED_FORMAT = -1, 148 DEFAULT_FORMAT = 0, 149 MAPPING_ID, 150 MAPPING_NAME, 151 USERMAP_CFG, 152 SMBUSERS 153 } format_t; 154 155 /* Gives the format to use. Set in print_mapping_init */ 156 static format_t pnm_format; 157 158 /* The file for print_mapping_init output. Mostly just stdout. */ 159 static FILE *pnm_file; 160 161 /* In smbusers format, more unixnames can be aggregated to one line. */ 162 static char *pnm_last_unixname; 163 164 /* 165 * idmap_api batch related variables: 166 * 167 * idmap can operate in two modes. It the batch mode, the idmap_api 168 * batch is commited at the end of a batch of several 169 * commands. At the end of input file, typically. This mode is used 170 * for processing input from a file. 171 * In the non-batch mode, each command is commited immediately. This 172 * mode is used for tty input. 173 */ 174 175 /* Are we in the batch mode? */ 176 static int batch_mode = 0; 177 178 /* Handles for idmap_api batch */ 179 static idmap_handle_t *handle = NULL; 180 static idmap_udt_handle_t *udt = NULL; 181 182 /* Do we need to commit the udt batch at the end? */ 183 static int udt_used; 184 185 /* Command handlers */ 186 187 static int do_show_mapping(flag_t *f, int argc, char **argv); 188 static int do_dump(flag_t *f, int argc, char **argv); 189 static int do_import(flag_t *f, int argc, char **argv); 190 static int do_list_name_mappings(flag_t *f, int argc, char **argv); 191 static int do_add_name_mapping(flag_t *f, int argc, char **argv); 192 static int do_remove_name_mapping(flag_t *f, int argc, char **argv); 193 static int do_exit(flag_t *f, int argc, char **argv); 194 static int do_export(flag_t *f, int argc, char **argv); 195 static int do_help(flag_t *f, int argc, char **argv); 196 197 /* Command names and their hanlers to be passed to idmap_engine */ 198 199 static cmd_ops_t commands[] = { 200 { 201 "show", 202 "c(create)", 203 do_show_mapping 204 }, 205 { 206 "dump", 207 "n(names)g(group)u(user)", 208 do_dump 209 }, 210 { 211 "import", 212 "F(flush)f:(file)", 213 do_import 214 }, 215 { 216 "export", 217 "f:(file)", 218 do_export 219 }, 220 { 221 "list", 222 "g(group)u(user)", 223 do_list_name_mappings 224 }, 225 { 226 "add", 227 "g(group)u(user)d(directional)", 228 do_add_name_mapping 229 }, 230 { 231 "remove", 232 "a(all)u(user)g(group)t(to)f(from)d(directional)", 233 do_remove_name_mapping 234 }, 235 { 236 "exit", 237 "", 238 do_exit 239 }, 240 { 241 "help", 242 "", 243 do_help 244 } 245 }; 246 247 248 /* 249 * Compare two strings just like strcmp, but stop before the end of 250 * the s2 251 */ 252 static int 253 strcmp_no0(const char *s1, const char *s2) { 254 return (strncmp(s1, s2, strlen(s2))); 255 } 256 257 /* The same as strcmp_no0, but case insensitive. */ 258 static int 259 strcasecmp_no0(const char *s1, const char *s2) { 260 return (strncasecmp(s1, s2, strlen(s2))); 261 } 262 263 264 /* Print help message */ 265 static void 266 help() { 267 (void) fprintf(stderr, 268 "idmap\n" 269 "idmap -f command-file\n" 270 "idmap show [-c] identity [targettype]\n" 271 "idmap dump [-u|-g] [-n]\n" 272 "idmap add -u|-g [-d] name1 name2\n" 273 "idmap remove -u|-g -a\n" 274 "idmap remove -u|-g name\n" 275 "idmap remove -u|-g [-d] name1 name2\n" 276 "idmap list [-u|-g]\n" 277 "idmap import [-F] [-f file] format\n" 278 "idmap export [-f file] format\n" 279 "idmap help\n"); 280 } 281 282 /* The handler for the "help" command. */ 283 static int 284 /* LINTED E_FUNC_ARG_UNUSED */ 285 do_help(flag_t *f, int argc, char **argv) 286 { 287 help(); 288 return (0); 289 } 290 291 /* Initialization of the idmap api batch */ 292 static int 293 init_batch() { 294 idmap_stat stat; 295 296 stat = idmap_init(&handle); 297 if (stat < 0) { 298 (void) fprintf(stderr, 299 gettext("Connection not established (%s)\n"), 300 idmap_stat2string(NULL, stat)); 301 return (-1); 302 } 303 304 return (0); 305 } 306 307 /* Initialization common to all commands */ 308 static int 309 init_command() { 310 if (batch_mode) 311 return (0); 312 313 return (init_batch()); 314 } 315 316 /* Finalization common to all commands */ 317 static void 318 fini_command() { 319 if (batch_mode) 320 return; 321 (void) idmap_fini(handle); 322 handle = NULL; 323 } 324 325 /* Initialization of the commands which perform write operations */ 326 static int 327 init_udt_batch() { 328 idmap_stat stat; 329 330 if (init_batch()) 331 return (-1); 332 333 stat = idmap_udt_create(handle, &udt); 334 if (stat < 0) { 335 (void) fprintf(stderr, 336 gettext("Error initiating transaction (%s)"), 337 idmap_stat2string(handle, stat)); 338 return (-1); 339 } 340 return (0); 341 } 342 343 344 /* Finalization of the write commands */ 345 static int 346 init_udt_command() { 347 udt_used = 1; 348 if (batch_mode) 349 return (0); 350 351 return (init_udt_batch()); 352 } 353 354 355 /* If everythings is OK, send the udt batch to idmapd */ 356 static void 357 fini_udt_command(int ok) { 358 idmap_stat stat; 359 360 if (batch_mode) 361 return; 362 if (udt == NULL) 363 return; 364 365 if (ok && udt_used) { 366 stat = idmap_udt_commit(udt); 367 if (stat < 0) { 368 (void) fprintf(stderr, 369 gettext("Error commiting transaction (%s)\n"), 370 idmap_stat2string(handle, stat)); 371 } 372 } 373 374 idmap_udt_destroy(udt); 375 udt = NULL; 376 udt_used = 0; 377 fini_command(); 378 } 379 380 /* Convert numeric expression of the direction to it's string form */ 381 static char * 382 direction2string(int direction) { 383 switch (direction) { 384 case DIR_BI: 385 return ("=="); 386 case DIR_W2U: 387 return ("=>"); 388 case DIR_U2W: 389 return ("<="); 390 default: 391 /* This can never happen: */ 392 (void) fprintf(stderr, 393 gettext("Internal error: invalid direction.\n")); 394 return (""); 395 } 396 /* never reached */ 397 } 398 399 /* Do we need quotation marks around winname in the USERMAP_CFG format? */ 400 static int 401 needs_protection(char *what) { 402 if (strchr(what, ' ') != NULL) 403 return (1); 404 405 if (strchr(what, '\t') != NULL) 406 return (1); 407 408 if (strchr(what, '#') != NULL) 409 return (1); 410 411 return (0); 412 } 413 414 415 /* 416 * Returns 1 if c is a shell-meta-character requiring quoting, 0 417 * otherwise. 418 * 419 * We don't quote '*' and ':' because they cannot do any harm 420 * a) they have no meaning to idmap_engine b) even ifsomebody copy & 421 * paste idmap output to a shell commandline, there is the identity 422 * type string in front of them. On the other hand, '*' and ':' are 423 * everywhere. 424 */ 425 static int 426 is_shell_special(char c) { 427 if (isspace(c)) 428 return (1); 429 430 if (strchr("&^{}#;'\"\\`!$()[]><|~", c) != NULL) 431 return (1); 432 433 return (0); 434 } 435 436 /* 437 * Quote any shell meta-characters in the given string. If 'quote' is 438 * true then use double-quotes to quote the whole string, else use 439 * back-slash to quote each individual meta-character. 440 * 441 * The resulting string is placed in *res. Callers must free *res if the 442 * return value isn't 0 (even if the given string had no meta-chars). 443 * If there are any errors this returns -1, else 0. 444 */ 445 static int 446 shell_app(char **res, char *string, int quote) { 447 int i, j; 448 uint_t noss = 0; /* Number Of Shell Special chars in the input */ 449 uint_t noqb = 0; /* Number Of Quotes and Backslahes in the input */ 450 char *out; 451 size_t len_orig = strlen(string); 452 size_t len; 453 454 /* First, let us count how many characters we need to quote: */ 455 for (i = 0; i < len_orig; i++) { 456 if (is_shell_special(string[i])) { 457 noss++; 458 if (string[i] == '"' || string[i] == '\\') 459 noqb++; 460 } 461 462 } 463 464 /* Do we need to quote at all? */ 465 if (noss == 0) { 466 out = strdup(string); 467 if (out == NULL) { 468 (void) fprintf(stderr, gettext("Not enough memory.\n")); 469 return (-1); 470 } 471 *res = out; 472 return (0); 473 } 474 475 /* What is the length of the result? */ 476 if (quote) 477 len = strlen(string) + 2 + noqb + 1; /* 2 for quotation marks */ 478 else 479 len = strlen(string) + noss + 1; 480 481 out = (char *)malloc(len * sizeof (char)); 482 if (out == NULL) { 483 (void) fprintf(stderr, gettext("Not enough memory.\n")); 484 return (-1); 485 } 486 487 j = 0; 488 if (quote) 489 out[j++] = '"'; 490 491 for (i = 0; i < len_orig; i++) { 492 /* Quote the dangerous chars by a backslash */ 493 if (quote && (string[i] == '"' || string[i] == '\\') || 494 (!quote && is_shell_special(string[i]))) { 495 out[j++] = '\\'; 496 } 497 out[j++] = string[i]; 498 } 499 500 if (quote) 501 out[j++] = '"'; 502 503 out[j] = '\0'; 504 *res = out; 505 return (0); 506 } 507 508 /* Assemble string form sid */ 509 static char * 510 sid_format(char *sidprefix, idmap_rid_t rid) { 511 char *to; 512 size_t len; 513 514 /* 'sid:' + sidprefix + '-' + rid + '\0' */ 515 len = strlen(sidprefix) + 6 + 3 * sizeof (rid); 516 to = (char *)malloc(len * sizeof (char)); 517 if (to == NULL) 518 return (NULL); 519 520 (void) snprintf(to, len, "sid:%s-%u", sidprefix, rid); 521 return (to); 522 } 523 524 /* Assemble string form uid or gid */ 525 static char * 526 pid_format(uid_t from, int is_user) { 527 char *to; 528 size_t len; 529 530 /* ID_UID ":" + uid + '\0' */ 531 len = 5 + 3 * sizeof (uid_t); 532 to = (char *)malloc(len * sizeof (char)); 533 if (to == NULL) 534 return (NULL); 535 536 (void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from); 537 return (to); 538 } 539 540 /* Assemble winname, e.g. "winname:bob@foo.sun.com", from name_mapping_t */ 541 static int 542 nm2winqn(name_mapping_t *nm, char **winqn) { 543 char *out; 544 size_t length = 0; 545 int is_domain = 1; 546 547 /* Sometimes there are no text names. Return a sid, then. */ 548 if (nm->winname == NULL) { 549 if (nm->sidprefix == NULL) 550 return (-1); 551 552 *winqn = sid_format(nm->sidprefix, nm->rid); 553 return (0); 554 } 555 556 length = strlen(ID_WINNAME ":") + strlen(nm->winname); 557 558 /* Windomain is not mandatory: */ 559 if (nm->windomain == NULL || 560 *nm->winname == '\0' || 561 strcmp(nm->winname, "\"\"") == 0) 562 is_domain = 0; 563 else 564 length += strlen(nm->windomain) + 1; 565 566 out = (char *)malloc((length + 1) * sizeof (char)); 567 if (out == NULL) { 568 (void) fprintf(stderr, 569 gettext("Not enough memory.\n")); 570 return (-1); 571 } 572 573 (void) strcpy(out, ID_WINNAME ":"); 574 575 if (!is_domain) 576 (void) strcat(out, nm->winname); 577 else if (nm->is_nt4) { 578 (void) strcat(out, nm->windomain); 579 (void) strcat(out, "\\"); 580 (void) strcat(out, nm->winname); 581 } else { 582 (void) strcat(out, nm->winname); 583 (void) strcat(out, "@"); 584 (void) strcat(out, nm->windomain); 585 } 586 587 *winqn = out; 588 return (0); 589 } 590 591 /* Assemble a text unixname, e.g. unixname:fred */ 592 static int 593 nm2unixname(name_mapping_t *nm, char **unixname) { 594 size_t length = 0; 595 char *out; 596 char *it; 597 598 /* Sometimes there is no name, just pid: */ 599 if (nm->unixname == NULL) { 600 if (nm->pid == UNDEFINED_UID) 601 return (-1); 602 603 *unixname = pid_format(nm->pid, nm->is_user); 604 return (0); 605 } 606 607 if (shell_app(&it, nm->unixname, 0)) 608 return (-1); 609 610 length = strlen(ID_UNIXNAME ":") + strlen(it); 611 612 out = (char *)malloc((length + 1) * sizeof (char)); 613 if (out == NULL) { 614 (void) fprintf(stderr, 615 gettext("Not enough memory.\n")); 616 free(it); 617 return (-1); 618 } 619 620 (void) strcpy(out, ID_UNIXNAME ":"); 621 (void) strcat(out, it); 622 free(it); 623 624 *unixname = out; 625 return (0); 626 } 627 628 /* Initialize print_mapping variables. Must be called before print_mapping */ 629 static int 630 print_mapping_init(format_t f, FILE *fi) { 631 pnm_format = f; 632 pnm_file = fi; 633 634 switch (pnm_format) { 635 case SMBUSERS: 636 pnm_last_unixname = NULL; 637 break; 638 default: 639 ; 640 } 641 642 return (0); 643 } 644 645 /* Finalize print_mapping. */ 646 static int 647 print_mapping_fini() { 648 switch (pnm_format) { 649 case SMBUSERS: 650 if (pnm_last_unixname != NULL) { 651 (void) fprintf(pnm_file, "\n"); 652 free(pnm_last_unixname); 653 } 654 break; 655 default: 656 ; 657 } 658 659 pnm_file = stderr; 660 pnm_format = UNDEFINED_FORMAT; 661 662 return (0); 663 } 664 665 /* 666 * This prints both name rules and ordinary mappings, based on the pnm_format 667 * set in print_mapping_init(). 668 */ 669 670 static int 671 print_mapping(name_mapping_t *nm) 672 { 673 char *dirstring; 674 char *winname_qm, *windomain_qm, *unixname_qm; 675 char type; 676 char *winname = NULL; 677 char *winname1 = NULL; 678 char *unixname = NULL; 679 FILE *f = pnm_file; 680 681 682 switch (pnm_format) { 683 case MAPPING_NAME: 684 if (nm2winqn(nm, &winname) < 0) 685 return (-1); 686 if (nm2unixname(nm, &unixname) < 0) { 687 free(winname); 688 return (-1); 689 } 690 /* LINTED E_CASE_FALLTHRU */ 691 case MAPPING_ID: 692 if (pnm_format == MAPPING_ID) { 693 if (nm->sidprefix == NULL) { 694 (void) fprintf(stderr, 695 gettext("SID not given.\n")); 696 return (-1); 697 } 698 winname = sid_format(nm->sidprefix, nm->rid); 699 if (winname == NULL) 700 return (-1); 701 unixname = pid_format(nm->pid, nm->is_user); 702 if (unixname == NULL) { 703 free(winname); 704 return (-1); 705 } 706 } 707 708 dirstring = direction2string(nm->direction); 709 710 (void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring, 711 unixname); 712 713 free(winname); 714 free(unixname); 715 break; 716 case SMBUSERS: 717 if (!nm->is_user) { 718 (void) fprintf(stderr, 719 gettext("Group rule: ")); 720 f = stderr; 721 } else if (nm->direction == DIR_U2W) { 722 (void) fprintf(stderr, 723 gettext("Opposite direction of the mapping: ")); 724 f = stderr; 725 } 726 if (shell_app(&winname, nm->winname, 1)) 727 return (-1); 728 729 if (pnm_file != f) { 730 (void) fprintf(f, "%s=%s\n", nm->unixname, winname); 731 } else if (pnm_last_unixname != NULL && 732 strcmp(pnm_last_unixname, nm->unixname) == 0) { 733 (void) fprintf(f, " %s", winname); 734 } else { 735 if (pnm_last_unixname != NULL) { 736 (void) fprintf(f, "\n"); 737 free(pnm_last_unixname); 738 } 739 pnm_last_unixname = strdup(nm->unixname); 740 (void) fprintf(f, "%s=%s", nm->unixname, winname); 741 } 742 743 free(winname); 744 745 break; 746 case USERMAP_CFG: 747 if (!nm->is_user) { 748 (void) fprintf(stderr, 749 gettext("Group rule: ")); 750 f = stderr; 751 } 752 753 dirstring = direction2string(nm->direction); 754 755 winname_qm = needs_protection(nm->winname) ? "\"" : ""; 756 windomain_qm = nm->windomain && 757 needs_protection(nm->windomain) ? "\"" : ""; 758 unixname_qm = needs_protection(nm->unixname) ? "\"" : ""; 759 760 if (nm->windomain == NULL) 761 (void) fprintf(f, "%s%s%s\t%s\t%s%s%s\n", 762 winname_qm, 763 nm->winname, winname_qm, dirstring, 764 unixname_qm, nm->unixname, unixname_qm); 765 else 766 (void) fprintf(f, nm->is_nt4 ? 767 "%s%s%1$s\\%3$s%4$s%3$s\t%5$s\t%6$s%7$s%6$s\n" : 768 "%3$s%4$s%3$s@%1$s%2$s%1$s\t%5$s\t%6$s%7$s%6$s\n", 769 windomain_qm, nm->windomain, 770 winname_qm, nm->winname, 771 dirstring, 772 unixname_qm, nm->unixname); 773 break; 774 775 case DEFAULT_FORMAT: 776 /* 'u', 'g' refer to -u, -g switch of idmap add */ 777 type = nm->is_user ? 'u' : 'g'; 778 if (nm2winqn(nm, &winname1) < 0) 779 return (-1); 780 781 if (shell_app(&winname, winname1, 0)) { 782 free(winname1); 783 return (-1); 784 } 785 786 free(winname1); 787 788 if (nm2unixname(nm, &unixname)) { 789 free(winname); 790 return (-1); 791 } 792 793 if (nm->direction == DIR_U2W) { 794 (void) fprintf(f, 795 "add -%c -d\t%s\t%s\n", 796 type, unixname, winname); 797 } else { 798 (void) fprintf(f, 799 "add -%c %s\t%s\t%s\n", 800 type, nm->direction == DIR_BI ? "" : "-d", 801 winname, unixname); 802 } 803 free(winname); 804 free(unixname); 805 break; 806 default: 807 /* This can never happen: */ 808 (void) fprintf(stderr, 809 gettext("Internal error: invalid print format.\n")); 810 return (-1); 811 } 812 813 return (0); 814 } 815 816 /* Allocate a new name_mapping_t and initialize the values. */ 817 static name_mapping_t * 818 name_mapping_init() { 819 name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t)); 820 if (nm == NULL) { 821 (void) fprintf(stderr, gettext("Not enough memory.\n")); 822 return (NULL); 823 } 824 nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL; 825 nm->rid = UNDEFINED_RID; 826 nm->is_nt4 = B_FALSE; 827 nm->is_user = I_UNKNOWN; 828 nm->direction = DIR_UNKNOWN; 829 nm->pid = UNDEFINED_UID; 830 return (nm); 831 } 832 833 /* Free name_mapping_t */ 834 static void 835 name_mapping_fini(name_mapping_t *nm) { 836 837 free(nm->winname); 838 free(nm->windomain); 839 free(nm->unixname); 840 free(nm->sidprefix); 841 842 free(nm); 843 } 844 845 /* Is there exactly one of -g, -u flags? */ 846 static int 847 is_type_determined(flag_t *f) 848 { 849 if (f[u_FLAG] == NULL && f[g_FLAG] == NULL || /* none */ 850 f[u_FLAG] != NULL && f[g_FLAG] != NULL) /* both */ { 851 (void) fprintf(stderr, 852 gettext("Type (-u|-g) not determined.\n")); 853 return (0); 854 } 855 return (1); 856 } 857 858 /* Does user request a user-related operation? */ 859 static int 860 is_user_wanted(flag_t *f) { 861 if (f[u_FLAG] != NULL || f[g_FLAG] == NULL) 862 return (1); 863 return (0); 864 } 865 866 /* Does user request a group-related operation? */ 867 static int 868 is_group_wanted(flag_t *f) { 869 if (f[g_FLAG] != NULL || f[u_FLAG] == NULL) 870 return (1); 871 return (0); 872 } 873 874 875 /* dump command handler */ 876 static int 877 /* LINTED E_FUNC_ARG_UNUSED */ 878 do_dump(flag_t *f, int argc, char **argv) 879 { 880 idmap_stat stat; 881 idmap_iter_t *ihandle; 882 int is_user; 883 int rc = 0; 884 885 if (init_command()) 886 return (-1); 887 888 (void) print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID, 889 stdout); 890 891 for (is_user = I_YES; is_user >= I_NO; is_user--) { 892 /* 893 * If there is exactly one of -u, -g flags, we print 894 * only that type. Otherwise both of them: 895 */ 896 if (!is_user_wanted(f) && is_user || 897 !is_group_wanted(f) && !is_user) 898 continue; 899 900 stat = idmap_iter_mappings(handle, is_user, &ihandle); 901 if (stat < 0) { 902 (void) fprintf(stderr, 903 gettext("Iteration handle not obtained (%s)\n"), 904 idmap_stat2string(handle, stat)); 905 rc = -1; 906 goto cleanup; 907 } 908 909 do { 910 name_mapping_t *nm = name_mapping_init(); 911 if (nm == NULL) { 912 rc = -1; 913 goto cleanup; 914 } 915 nm->is_user = is_user; 916 917 918 stat = idmap_iter_next_mapping(ihandle, 919 &nm->sidprefix, &nm->rid, &nm->pid, 920 &nm->winname, &nm->windomain, 921 &nm->unixname, &nm->direction); 922 923 if (stat >= 0) 924 (void) print_mapping(nm); 925 926 name_mapping_fini(nm); 927 928 } while (stat > 0); 929 930 /* IDMAP_ERR_NOTFOUND indicates end of the list */ 931 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 932 (void) fprintf(stderr, 933 gettext("Error during iteration (%s)\n"), 934 idmap_stat2string(handle, stat)); 935 rc = -1; 936 goto cleanup; 937 } 938 939 idmap_iter_destroy(ihandle); 940 } 941 cleanup: 942 (void) print_mapping_fini(); 943 fini_command(); 944 return (rc); 945 } 946 947 /* 948 * The same as strdup, but length chars is duplicated, no matter on 949 * '\0'. The caller must guarantee "length" chars in "from". 950 */ 951 static char * 952 strndup(char *from, size_t length) { 953 char *out = (char *)malloc((length + 1) * sizeof (char)); 954 if (out == NULL) { 955 (void) fprintf(stderr, gettext("Not enough memory\n")); 956 return (NULL); 957 } 958 (void) strncpy(out, from, length); 959 out[length] = '\0'; 960 return (out); 961 } 962 963 /* 964 * Convert pid from string to it's numerical representation. If it is 965 * a valid string, i.e. number of a proper length, return 1. Otherwise 966 * print an error message and return 0. 967 */ 968 static int 969 pid_convert(char *string, uid_t *number, int type) { 970 int i; 971 long long ll; 972 char *type_string; 973 size_t len = strlen(string); 974 975 if (type == TYPE_GID) 976 type_string = ID_GID; 977 else if (type == TYPE_UID) 978 type_string = ID_UID; 979 else 980 return (0); 981 982 for (i = 0; i < len; i++) { 983 if (!isdigit(string[i])) { 984 (void) fprintf(stderr, 985 gettext("\"%s\" is not a valid %s: the non-digit" 986 " character '%c' found.\n"), string, 987 type_string, string[i]); 988 return (0); 989 } 990 } 991 992 ll = atoll(string); 993 994 /* Isn't it too large? */ 995 if (type == TYPE_UID && (uid_t)ll != ll || 996 type == TYPE_GID && (gid_t)ll != ll) { 997 (void) fprintf(stderr, 998 gettext("%llu: too large for a %s.\n"), ll, 999 type_string); 1000 return (0); 1001 } 1002 1003 *number = (uid_t)ll; 1004 return (1); 1005 } 1006 1007 /* 1008 * Convert SID from string to prefix and rid. If it has a valid 1009 * format, i.e. S(\-\d+)+, return 1. Otherwise print an error 1010 * message and return 0. 1011 */ 1012 static int 1013 sid_convert(char *from, char **prefix, idmap_rid_t *rid) { 1014 int i, j; 1015 char *cp; 1016 char *ecp; 1017 char *prefix_end; 1018 u_longlong_t a; 1019 unsigned long r; 1020 1021 if (strcmp_no0(from, "S-1-") != 0) { 1022 (void) fprintf(stderr, 1023 gettext("Invalid %s \"%s\": it doesn't start " 1024 "with \"%s\".\n"), ID_SID, from, "S-1-"); 1025 return (0); 1026 } 1027 1028 if (strlen(from) <= strlen("S-1-")) { 1029 (void) fprintf(stderr, 1030 gettext("Invalid %s \"%s\": the authority and RID parts are" 1031 " missing.\n"), 1032 ID_SID, from); 1033 return (0); 1034 } 1035 1036 /* count '-'s */ 1037 for (j = 0, cp = strchr(from, '-'); 1038 cp != NULL; 1039 j++, cp = strchr(cp + 1, '-')) { 1040 /* can't end on a '-' */ 1041 if (*(cp + 1) == '\0') { 1042 (void) fprintf(stderr, 1043 gettext("Invalid %s \"%s\": '-' at the end.\n"), 1044 ID_SID, from); 1045 return (0); 1046 } else if (*(cp + 1) == '-') { 1047 (void) fprintf(stderr, 1048 gettext("Invalid %s \"%s\": double '-'.\n"), 1049 ID_SID, from); 1050 return (0); 1051 } 1052 } 1053 1054 1055 /* check that we only have digits and '-' */ 1056 i = strspn(from + 1, "0123456789-") + 1; 1057 if (i < strlen(from)) { 1058 (void) fprintf(stderr, 1059 gettext("Invalid %s \"%s\": invalid character '%c'.\n"), 1060 ID_SID, from, from[i]); 1061 return (0); 1062 } 1063 1064 1065 cp = from + strlen("S-1-"); 1066 1067 /* 64-bit safe parsing of unsigned 48-bit authority value */ 1068 errno = 0; 1069 a = strtoull(cp, &ecp, 10); 1070 1071 /* errors parsing the authority or too many bits */ 1072 if (cp == ecp || (a == 0 && errno == EINVAL)) { 1073 (void) fprintf(stderr, 1074 gettext("Invalid %s \"%s\": unable to parse the " 1075 "authority \"%.*s\".\n"), ID_SID, from, ecp - cp, 1076 cp); 1077 return (0); 1078 } 1079 1080 if ((a == ULLONG_MAX && errno == ERANGE) || 1081 (a & 0x0000ffffffffffffULL) != a) { 1082 (void) fprintf(stderr, 1083 gettext("Invalid %s \"%s\": the authority " 1084 "\"%.*s\" is too large.\n"), ID_SID, from, 1085 ecp - cp, cp); 1086 return (0); 1087 } 1088 1089 cp = ecp; 1090 1091 if (j < 3) { 1092 (void) fprintf(stderr, 1093 gettext("Invalid %s \"%s\": must have at least one RID.\n"), 1094 ID_SID, from); 1095 return (0); 1096 } 1097 1098 for (i = 2; i < j; i++) { 1099 if (*cp++ != '-') { 1100 /* Should never happen */ 1101 (void) fprintf(stderr, 1102 gettext("Invalid %s \"%s\": internal error:" 1103 " '-' missing.\n"), 1104 ID_SID, from); 1105 return (0); 1106 } 1107 /* 32-bit safe parsing of unsigned 32-bit RID */ 1108 errno = 0; 1109 r = strtoul(cp, &ecp, 10); 1110 1111 /* errors parsing the RID */ 1112 if (cp == ecp || (r == 0 && errno == EINVAL)) { 1113 /* should never happen */ 1114 (void) fprintf(stderr, 1115 gettext("Invalid %s \"%s\": internal error: " 1116 "unable to parse the RID " 1117 "after \"%.*s\".\n"), ID_SID, 1118 from, cp - from, from); 1119 return (0); 1120 } 1121 1122 if (r == ULONG_MAX && errno == ERANGE) { 1123 (void) fprintf(stderr, 1124 gettext("Invalid %s \"%s\": the RID \"%.*s\"" 1125 " is too large.\n"), ID_SID, 1126 from, ecp - cp, cp); 1127 return (0); 1128 } 1129 prefix_end = cp; 1130 cp = ecp; 1131 } 1132 1133 /* check that all of the string SID has been consumed */ 1134 if (*cp != '\0') { 1135 /* Should never happen */ 1136 (void) fprintf(stderr, 1137 gettext("Invalid %s \"%s\": internal error: " 1138 "something is still left.\n"), 1139 ID_SID, from); 1140 return (0); 1141 } 1142 1143 *rid = (idmap_rid_t)r; 1144 1145 /* -1 for the '-' at the end: */ 1146 *prefix = strndup(from, prefix_end - from - 1); 1147 if (*prefix == NULL) { 1148 (void) fprintf(stderr, 1149 gettext("Not enough memory.\n")); 1150 return (0); 1151 } 1152 1153 return (1); 1154 } 1155 1156 /* Does the line start with USERMAP_CFG IP qualifier? */ 1157 static int 1158 ucp_is_IP_qualifier(char *line) { 1159 char *it; 1160 it = line + strcspn(line, " \t\n#:"); 1161 return (*(it + 1) == ':' ? 1 : 0); 1162 } 1163 1164 1165 /* 1166 * returns interior of quotation marks in USERMAP_CFG. In this format, 1167 * there cannot be a protected quotation mark inside. 1168 */ 1169 static char * 1170 ucp_qm_interior(char **line, int line_num) { 1171 char *out; 1172 char *qm = strchr(*line + 1, '"'); 1173 if (qm == NULL) { 1174 (void) fprintf(stderr, 1175 gettext("Line %d: Unclosed quotations\n"), 1176 line_num); 1177 return (NULL); 1178 } 1179 1180 out = strndup(*line + 1, qm - *line - 1); 1181 *line = qm + 1; 1182 return (out); 1183 } 1184 1185 /* 1186 * Grab next token from the line in USERMAP_CFG format. terminators, 1187 * the 3rd parameter, contains all the characters which can terminate 1188 * the token. line_num is the line number of input used for error 1189 * reporting. 1190 */ 1191 static char * 1192 ucp_grab_token(char **line, int line_num, const char *terminators) { 1193 char *token; 1194 if (**line == '"') 1195 token = ucp_qm_interior(line, line_num); 1196 else { 1197 int length = strcspn(*line, terminators); 1198 token = strndup(*line, length); 1199 *line += length; 1200 } 1201 1202 return (token); 1203 } 1204 1205 1206 /* 1207 * Convert a line in usermap.cfg format to name_mapping. line_num is 1208 * the line number of input used for error reporting. 1209 * 1210 * Return values: -1 for error, 0 for empty line, 1 for a mapping 1211 * found. 1212 */ 1213 static int 1214 ucp_line2nm(char *line, int line_num, name_mapping_t *nm) { 1215 char *it; 1216 char *token; 1217 char *token2; 1218 char separator; 1219 int is_direction = 0; 1220 1221 it = line + strspn(line, " \t\n"); 1222 1223 /* empty or comment lines are OK: */ 1224 if (*it == '\0' || *it == '#') 1225 return (0); 1226 1227 /* We do not support network qualifiers */ 1228 if (ucp_is_IP_qualifier(it)) { 1229 (void) fprintf(stderr, 1230 gettext("Line %d: unable to handle network qualifier.\n"), 1231 line_num); 1232 return (-1); 1233 } 1234 1235 /* The windows name: */ 1236 token = ucp_grab_token(&it, line_num, " \t#\\\n@=<"); 1237 if (token == NULL) 1238 return (-1); 1239 1240 separator = *it; 1241 1242 /* Didn't we bump to the end of line? */ 1243 if (separator == '\0' || separator == '#') { 1244 free(token); 1245 (void) fprintf(stderr, 1246 gettext("Line %d: UNIX_name not found.\n"), 1247 line_num); 1248 return (-1); 1249 } 1250 1251 /* Do we have a domainname? */ 1252 if (separator == '\\' || separator == '@') { 1253 it ++; 1254 token2 = ucp_grab_token(&it, line_num, " \t\n#"); 1255 if (token2 == NULL) { 1256 free(token); 1257 return (-1); 1258 } else if (*it == '\0' || *it == '#') { 1259 free(token); 1260 free(token2); 1261 (void) fprintf(stderr, 1262 gettext("Line %d: UNIX_name not found.\n"), 1263 line_num); 1264 } 1265 1266 if (separator == '\\') { 1267 nm->windomain = token; 1268 nm->winname = token2; 1269 nm->is_nt4 = 1; 1270 } else { 1271 nm->windomain = token2; 1272 nm->winname = token; 1273 nm->is_nt4 = 0; 1274 1275 } 1276 } else { 1277 nm->windomain = NULL; 1278 nm->winname = token; 1279 nm->is_nt4 = 0; 1280 } 1281 1282 1283 it = it + strspn(it, " \t\n"); 1284 1285 /* Direction string is optional: */ 1286 if (strncmp(it, "==", 2) == 0) { 1287 nm->direction = DIR_BI; 1288 is_direction = 1; 1289 } else if (strncmp(it, "<=", 2) == 0) { 1290 nm->direction = DIR_U2W; 1291 is_direction = 1; 1292 } else if (strncmp(it, "=>", 2) == 0) { 1293 nm->direction = DIR_W2U; 1294 is_direction = 1; 1295 } else { 1296 nm->direction = DIR_BI; 1297 is_direction = 0; 1298 } 1299 1300 if (is_direction) { 1301 it += 2; 1302 it += strspn(it, " \t\n"); 1303 1304 if (*it == '\0' || *it == '#') { 1305 (void) fprintf(stderr, 1306 gettext("Line %d: UNIX_name not found.\n"), 1307 line_num); 1308 return (-1); 1309 } 1310 } 1311 1312 /* Now unixname: */ 1313 it += strspn(it, " \t\n"); 1314 token = ucp_grab_token(&it, line_num, " \t\n#"); 1315 1316 if (token == NULL) 1317 /* nm->winname to be freed by name_mapping_fini */ 1318 return (-1); 1319 1320 /* Neither here we support IP qualifiers */ 1321 if (ucp_is_IP_qualifier(token)) { 1322 (void) fprintf(stderr, 1323 gettext("Line %d: unable to handle network qualifier.\n"), 1324 line_num); 1325 free(token); 1326 return (-1); 1327 } 1328 1329 nm->unixname = token; 1330 1331 it += strspn(it, " \t\n"); 1332 1333 /* Does something remain on the line */ 1334 if (*it != '\0' && *it != '#') { 1335 (void) fprintf(stderr, 1336 gettext("Line %d: unrecognized parameters \"%s\".\n"), 1337 line_num, it); 1338 return (-1); 1339 } 1340 1341 return (1); 1342 } 1343 1344 /* 1345 * Parse SMBUSERS line to name_mapping_t. if line is NULL, then 1346 * pasrsing of the previous line is continued. line_num is input line 1347 * number used for error reporting. 1348 * Return values: 1349 * rc -1: error 1350 * rc = 0: mapping found and the line is finished, 1351 * rc = 1: mapping found and there remains other on the line 1352 */ 1353 static int 1354 sup_line2nm(char *line, int line_num, name_mapping_t *nm) { 1355 static char *ll = NULL; 1356 static char *unixname = NULL; 1357 static size_t unixname_l = 0; 1358 char *token; 1359 1360 if (line != NULL) { 1361 ll = line; 1362 1363 unixname = ll += strspn(ll, " \t"); 1364 if (*ll == '\0' || *ll == '#') 1365 return (0); 1366 1367 unixname_l = strcspn(ll, " \t:=#\n"); 1368 ll += unixname_l; 1369 1370 if (*ll == '\0'|| *ll == '#') 1371 return (0); 1372 1373 ll += strspn(ll, " \t:=#\n"); 1374 1375 } 1376 1377 if (*ll == '\0'|| *ll == '#') 1378 return (0); 1379 1380 token = ucp_grab_token(&ll, line_num, " \t\n"); 1381 if (token == NULL) 1382 return (-1); 1383 1384 nm->is_nt4 = 0; 1385 nm->direction = DIR_W2U; 1386 1387 nm->windomain = NULL; 1388 nm->winname = token; 1389 nm->unixname = strndup(unixname, unixname_l); 1390 if (nm->unixname == NULL) 1391 return (-1); 1392 1393 ll += strspn(ll, " \t\n"); 1394 return (1); 1395 } 1396 1397 /* Parse line to name_mapping_t. Basicaly just a format switch. */ 1398 static int 1399 line2nm(char *line, int line_num, name_mapping_t *nm, format_t f) { 1400 switch (f) { 1401 case USERMAP_CFG: 1402 if (line == NULL) 1403 return (0); 1404 else 1405 return (ucp_line2nm(line, line_num, nm)); 1406 case SMBUSERS: 1407 return (sup_line2nm(line, line_num, nm)); 1408 default: 1409 /* This can never happen */ 1410 (void) fprintf(stderr, 1411 gettext("Internal error: invalid line format.\n")); 1412 } 1413 1414 return (-1); 1415 } 1416 1417 1418 /* Examine -f flag and return the appropriate format_t */ 1419 static format_t 1420 ff2format(char *ff, int is_mandatory) { 1421 1422 if (ff == NULL && is_mandatory) { 1423 (void) fprintf(stderr, gettext("Format not given.\n")); 1424 return (UNDEFINED_FORMAT); 1425 } 1426 1427 if (ff == NULL) 1428 return (DEFAULT_FORMAT); 1429 1430 if (strcasecmp(ff, "usermap.cfg") == 0) 1431 return (USERMAP_CFG); 1432 1433 if (strcasecmp(ff, "smbusers") == 0) 1434 return (SMBUSERS); 1435 1436 (void) fprintf(stderr, 1437 gettext("The only known formats are: \"usermap.cfg\" and " 1438 "\"smbusers\".\n")); 1439 return (UNDEFINED_FORMAT); 1440 } 1441 1442 /* Delete all namerules of the given type */ 1443 static int 1444 flush_nm(boolean_t is_user) 1445 { 1446 idmap_stat stat; 1447 1448 stat = idmap_udt_flush_namerules(udt, is_user); 1449 if (stat < 0) { 1450 (void) fprintf(stderr, 1451 is_user ? gettext("Unable to flush users (%s).\n") 1452 : gettext("Unable to flush groups (%s).\n"), 1453 idmap_stat2string(handle, stat)); 1454 return (-1); 1455 } 1456 return (0); 1457 } 1458 1459 /* import command handler */ 1460 static int 1461 /* LINTED E_FUNC_ARG_UNUSED */ 1462 do_import(flag_t *f, int argc, char **argv) 1463 { 1464 name_mapping_t *nm; 1465 char line[MAX_INPUT_LINE_SZ]; 1466 format_t format; 1467 int rc = 0; 1468 idmap_stat stat; 1469 int line_num; 1470 FILE *file = NULL; 1471 1472 if (batch_mode) { 1473 (void) fprintf(stderr, 1474 gettext("Import is not allowed in the batch mode.\n")); 1475 return (-1); 1476 } 1477 1478 format = ff2format(argv[0], 1); 1479 if (format == UNDEFINED_FORMAT) 1480 return (-1); 1481 1482 if (init_udt_command()) 1483 return (-1); 1484 1485 /* We don't flush groups in the usermap.cfg nor smbusers format */ 1486 if (f[F_FLAG] != NULL && 1487 flush_nm(B_TRUE) < 0 && 1488 (format == USERMAP_CFG || format == SMBUSERS || 1489 flush_nm(B_FALSE) < 0)) { 1490 rc = -1; 1491 goto cleanup; 1492 } 1493 1494 line_num = 0; 1495 1496 /* Where we import from? */ 1497 if (f[f_FLAG] == NULL) 1498 file = stdin; 1499 else { 1500 file = fopen(f[f_FLAG], "r"); 1501 if (file == NULL) { 1502 perror(f[f_FLAG]); 1503 goto cleanup; 1504 } 1505 } 1506 1507 1508 while (fgets(line, MAX_INPUT_LINE_SZ, file)) { 1509 char *line2 = line; 1510 line_num++; 1511 1512 /* 1513 * In SMBUSERS format there can be more mappings on 1514 * each line. So we need the internal cycle for each line. 1515 */ 1516 do { 1517 nm = name_mapping_init(); 1518 if (nm == NULL) { 1519 rc = -1; 1520 goto cleanup; 1521 } 1522 1523 rc = line2nm(line2, line_num, nm, format); 1524 line2 = NULL; 1525 1526 if (rc < 1) { 1527 name_mapping_fini(nm); 1528 break; 1529 } 1530 1531 stat = idmap_udt_add_namerule(udt, nm->windomain, 1532 nm->is_user ? B_TRUE : B_FALSE, nm->winname, 1533 nm->unixname, nm->is_nt4, nm->direction); 1534 if (stat < 0) { 1535 (void) fprintf(stderr, 1536 gettext("Transaction error (%s)\n"), 1537 idmap_stat2string(handle, stat)); 1538 rc = -1; 1539 } 1540 1541 name_mapping_fini(nm); 1542 1543 } while (rc >= 0); 1544 1545 if (rc < 0) { 1546 (void) fprintf(stderr, 1547 gettext("Import canceled.\n")); 1548 break; 1549 } 1550 } 1551 1552 cleanup: 1553 fini_udt_command(rc < 0 ? 0 : 1); 1554 if (file != NULL && file != stdin) 1555 (void) fclose(file); 1556 return (rc); 1557 } 1558 1559 1560 /* 1561 * List name mappings in the format specified. list_users / 1562 * list_groups determine which type to list. The output goes to the 1563 * file fi. 1564 */ 1565 static int 1566 list_name_mappings(int list_users, int list_groups, format_t format, FILE *fi) 1567 { 1568 idmap_stat stat; 1569 idmap_iter_t *ihandle; 1570 name_mapping_t *nm; 1571 int is_user; 1572 1573 for (is_user = I_YES; is_user >= I_NO; is_user--) { 1574 if (is_user && !list_users) 1575 continue; 1576 if (!is_user && !list_groups) 1577 continue; 1578 /* Only users can be in USERMAP_CFG format, not a group */ 1579 if (!is_user && format == USERMAP_CFG) 1580 continue; 1581 1582 stat = idmap_iter_namerules(handle, NULL, is_user, NULL, 1583 NULL, &ihandle); 1584 if (stat < 0) { 1585 (void) fprintf(stderr, 1586 gettext("Iteration handle not obtained (%s)\n"), 1587 idmap_stat2string(handle, stat)); 1588 idmap_iter_destroy(ihandle); 1589 return (-1); 1590 } 1591 1592 (void) print_mapping_init(format, fi); 1593 1594 do { 1595 nm = name_mapping_init(); 1596 if (nm == NULL) { 1597 idmap_iter_destroy(ihandle); 1598 return (-1); 1599 } 1600 1601 stat = idmap_iter_next_namerule(ihandle, &nm->windomain, 1602 &nm->winname, &nm->unixname, &nm->is_nt4, 1603 &nm->direction); 1604 if (stat >= 0) { 1605 nm->is_user = is_user; 1606 (void) print_mapping(nm); 1607 } 1608 1609 name_mapping_fini(nm); 1610 1611 } while (stat > 0); 1612 1613 (void) print_mapping_fini(); 1614 1615 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 1616 (void) fprintf(stderr, 1617 gettext("Error during iteration (%s)\n"), 1618 idmap_stat2string(handle, stat)); 1619 idmap_iter_destroy(ihandle); 1620 return (-1); 1621 } 1622 1623 idmap_iter_destroy(ihandle); 1624 } 1625 return (0); 1626 } 1627 1628 /* Export command handler */ 1629 static int 1630 /* LINTED E_FUNC_ARG_UNUSED */ 1631 do_export(flag_t *f, int argc, char **argv) { 1632 int rc; 1633 format_t format; 1634 FILE *fi; 1635 1636 format = ff2format(argv[0], 1); 1637 if (format == UNDEFINED_FORMAT) 1638 return (-1); 1639 1640 /* Where do we output to? */ 1641 if (f[f_FLAG] == NULL) 1642 fi = stdout; 1643 else { 1644 fi = fopen(f[f_FLAG], "w"); 1645 if (fi == NULL) { 1646 perror(f[f_FLAG]); 1647 return (-1); 1648 } 1649 } 1650 1651 if (init_command() < 0) { 1652 rc = -1; 1653 goto cleanup; 1654 } 1655 1656 /* List the requested types: */ 1657 rc = list_name_mappings(is_user_wanted(f), 1658 is_group_wanted(f), 1659 format, 1660 fi); 1661 1662 fini_command(); 1663 1664 cleanup: 1665 if (fi != NULL && fi != stdout) 1666 (void) fclose(fi); 1667 return (rc); 1668 } 1669 1670 /* List command handler */ 1671 static int 1672 /* LINTED E_FUNC_ARG_UNUSED */ 1673 do_list_name_mappings(flag_t *f, int argc, char **argv) 1674 { 1675 int rc; 1676 1677 if (init_command()) { 1678 return (-1); 1679 } 1680 1681 /* List the requested types: */ 1682 rc = list_name_mappings(is_user_wanted(f), 1683 is_group_wanted(f), 1684 DEFAULT_FORMAT, 1685 stdout); 1686 1687 fini_command(); 1688 return (rc); 1689 } 1690 1691 /* This is just a debug function for dumping flags */ 1692 static void 1693 print_flags(flag_t *f) 1694 { 1695 int c; 1696 for (c = 0; c < FLAG_ALPHABET_SIZE; c++) { 1697 if (f[c] == FLAG_SET) 1698 (void) printf("FLAG: -%c, VALUE: %p\n", c, 1699 (void *) f[c]); 1700 else if (f[c]) 1701 (void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]); 1702 } 1703 } 1704 1705 /* 1706 * This function splits name to the relevant pieces: is_user, winname, 1707 * windomain unixname. Sometimes it is not possible to determine OS 1708 * side, because it could be determined by the opposite name in idmap 1709 * show. So this function must be called several times. 1710 * 1711 * Return values: -1 ... clear syntax error 1712 * 0 ... it wasnt possible to determine 1713 * 1 ... determined 1714 */ 1715 static int 1716 name2parts(char *name, name_mapping_t *nm) { 1717 char *it; 1718 int is_win = I_NO; 1719 int is_unix = I_NO; 1720 1721 if (nm->winname != NULL && nm->unixname != NULL) 1722 return (0); 1723 1724 /* If it starts with type string, that is easy: */ 1725 if (it = strchr(name, ':')) { 1726 if (strcmp_no0(name, ID_UNIXNAME ":") == 0) { 1727 if (nm->unixname != NULL) 1728 return (0); 1729 is_unix = I_YES; 1730 } else if (strcmp_no0(name, ID_WINNAME ":") == 0) { 1731 if (nm->winname != NULL) 1732 return (0); 1733 is_win = I_YES; 1734 } else { 1735 (void) fprintf(stderr, 1736 gettext("Error: invalid identity type\n")); 1737 return (-1); 1738 } 1739 name = it + 1; 1740 } 1741 1742 /* If it contains '@' or '\\', then it is a winname with domain */ 1743 if (!is_unix && nm->winname == NULL) { 1744 if ((it = strchr(name, '@')) != NULL) { 1745 int length = it-name+1; 1746 nm->winname = (char *)malloc(length * sizeof (char)); 1747 (void) strncpy(nm->winname, name, length - 1); 1748 nm->winname[length - 1] = '\0'; 1749 nm->windomain = strdup(it + 1); 1750 return (1); 1751 } else if ((it = strrchr(name, '\\')) != NULL) { 1752 int length = it-name+1; 1753 nm->windomain = (char *)malloc(length * sizeof (char)); 1754 (void) strncpy(nm->windomain, name, length - 1); 1755 nm->windomain[length - 1] = '\0'; 1756 nm->winname = strdup(it + 1); 1757 nm->is_nt4 = B_TRUE; 1758 return (1); 1759 } 1760 } 1761 1762 /* 1763 * if is_unix/is_win is not yet determined, then the last 1764 * hope is that the opposite side is known already. In that 1765 * case, it is the only remaining side. 1766 */ 1767 if (is_unix || nm->unixname == NULL && nm->winname != NULL) { 1768 if (strlen(name) == 0) 1769 nm->unixname = strdup("\"\""); 1770 else 1771 nm->unixname = strdup(name); 1772 return (1); 1773 } else if (is_win || nm->unixname != NULL && nm->winname == NULL) { 1774 if (strlen(name) == 0) 1775 nm->winname = strdup("\"\""); 1776 else 1777 nm->winname = strdup(name); 1778 nm->windomain = NULL; 1779 return (1); 1780 } 1781 1782 return (0); 1783 } 1784 1785 /* add command handler. */ 1786 static int 1787 do_add_name_mapping(flag_t *f, int argc, char **argv) 1788 { 1789 name_mapping_t *nm; 1790 int rc = 0; 1791 int i; 1792 int is_argv0_unix = -1; 1793 idmap_stat stat; 1794 1795 1796 /* Two arguments and exactly one of -u, -g must be specified */ 1797 if (argc < 2) { 1798 (void) fprintf(stderr, gettext("Not enough arguments.\n")); 1799 return (-1); 1800 } else if (argc > 2) { 1801 (void) fprintf(stderr, gettext("Too many arguments.\n")); 1802 return (-1); 1803 } else if (!is_type_determined(f)) 1804 return (-1); 1805 1806 /* 1807 * Direction can be determined by the opposite name, so we 1808 * need to run name2parts twice for the first name, i.e. 3x in 1809 * total. 1810 */ 1811 nm = name_mapping_init(); 1812 if (nm == NULL) 1813 return (-1); 1814 1815 nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO; 1816 1817 for (i = 0; i < 3; i++) { 1818 switch (name2parts(argv[i % 2], nm)) { 1819 case -1: 1820 name_mapping_fini(nm); 1821 return (-1); 1822 case 1: 1823 if (is_argv0_unix < 0) 1824 is_argv0_unix = 1825 i % 2 ^ (nm->unixname != NULL ? 1 : 0); 1826 break; 1827 } 1828 } 1829 1830 if (nm->winname == NULL || nm->unixname == NULL) { 1831 (void) fprintf(stderr, gettext("Name types not determined.\n")); 1832 name_mapping_fini(nm); 1833 return (-1); 1834 } 1835 1836 if (f[d_FLAG] != NULL) 1837 nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U; 1838 else 1839 nm->direction = DIR_BI; 1840 1841 /* Now let us write it: */ 1842 1843 if (init_udt_command()) { 1844 name_mapping_fini(nm); 1845 return (-1); 1846 } 1847 1848 stat = idmap_udt_add_namerule(udt, nm->windomain, 1849 nm->is_user ? B_TRUE : B_FALSE, nm->winname, nm->unixname, 1850 nm->is_nt4, nm->direction); 1851 1852 /* We echo the mapping */ 1853 (void) print_mapping_init(DEFAULT_FORMAT, stdout); 1854 (void) print_mapping(nm); 1855 (void) print_mapping_fini(); 1856 1857 if (stat < 0) { 1858 (void) fprintf(stderr, 1859 gettext("Mapping not created (%s)\n"), 1860 idmap_stat2string(handle, stat)); 1861 rc = -1; 1862 } 1863 1864 cleanup: 1865 name_mapping_fini(nm); 1866 fini_udt_command(1); 1867 return (rc); 1868 } 1869 1870 /* remove command handler */ 1871 static int 1872 do_remove_name_mapping(flag_t *f, int argc, char **argv) 1873 { 1874 name_mapping_t *nm; 1875 int rc = 0; 1876 int i; 1877 int is_argv0_unix = -1; 1878 idmap_stat stat; 1879 1880 /* "-a" means we flush all of them */ 1881 if (f[a_FLAG] != NULL) { 1882 if (argc) { 1883 (void) fprintf(stderr, 1884 gettext("Too many arguments.\n")); 1885 return (-1); 1886 } 1887 1888 if (!is_type_determined(f)) 1889 return (-1); 1890 1891 if (init_udt_command()) 1892 return (-1); 1893 rc = flush_nm(f[u_FLAG] != NULL ? B_TRUE : B_FALSE); 1894 1895 fini_udt_command(rc ? 0 : 1); 1896 return (rc); 1897 } 1898 1899 /* Contrary to add_name_mapping, we can have only one argument */ 1900 if (argc < 1) { 1901 (void) fprintf(stderr, gettext("Not enough arguments.\n")); 1902 return (-1); 1903 } else if (argc > 2) { 1904 (void) fprintf(stderr, gettext("Too many arguments.\n")); 1905 return (-1); 1906 } else if (!is_type_determined(f)) { 1907 return (-1); 1908 } else if ( 1909 /* both -f and -t: */ 1910 f[f_FLAG] != NULL && f[t_FLAG] != NULL || 1911 /* -d with a single argument: */ 1912 argc == 1 && f[d_FLAG] != NULL || 1913 /* -f or -t with two arguments: */ 1914 argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) { 1915 (void) fprintf(stderr, 1916 gettext("Direction ambiguous.\n")); 1917 return (-1); 1918 } 1919 1920 1921 /* 1922 * Similar to do_add_name_mapping - see the comments 1923 * there. Except we may have only one argument here. 1924 */ 1925 nm = name_mapping_init(); 1926 if (nm == NULL) 1927 return (-1); 1928 1929 nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO; 1930 1931 for (i = 0; i < 2 * argc - 1; i++) { 1932 switch (name2parts(argv[i % 2], nm)) { 1933 case -1: 1934 name_mapping_fini(nm); 1935 return (-1); 1936 case 1: 1937 if (is_argv0_unix < 0) 1938 is_argv0_unix = i % 2 ^ (nm->unixname ? 1 : 0); 1939 break; 1940 } 1941 } 1942 1943 1944 if (nm->winname == NULL && nm->unixname == NULL) { 1945 (void) fprintf(stderr, gettext("Name types not determined.\n")); 1946 name_mapping_fini(nm); 1947 return (-1); 1948 } 1949 1950 /* 1951 * If the direction is not specified by a -d/-f/-t flag, then it 1952 * is DIR_UNKNOWN, because in that case we want to remove any 1953 * mapping. If it was DIR_BI, idmap_api would delete a 1954 * bidirectional one only. 1955 */ 1956 if (f[d_FLAG] != NULL || f[f_FLAG] != NULL) 1957 nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U; 1958 else if (f[t_FLAG] != NULL) 1959 nm->direction = is_argv0_unix ? DIR_W2U : DIR_U2W; 1960 else 1961 nm->direction = DIR_UNKNOWN; 1962 1963 if (init_udt_command()) { 1964 name_mapping_fini(nm); 1965 return (-1); 1966 } 1967 1968 stat = idmap_udt_rm_namerule(udt, nm->is_user ? B_TRUE : B_FALSE, 1969 nm->windomain, nm->winname, nm->unixname, nm->direction); 1970 1971 if (stat < 0) { 1972 (void) fprintf(stderr, 1973 gettext("Mapping not deleted (%s)\n"), 1974 idmap_stat2string(handle, stat)); 1975 rc = -1; 1976 } 1977 1978 cleanup: 1979 name_mapping_fini(nm); 1980 fini_udt_command(1); 1981 return (rc); 1982 } 1983 1984 1985 /* exit command handler */ 1986 static int 1987 /* LINTED E_FUNC_ARG_UNUSED */ 1988 do_exit(flag_t *f, int argc, char **argv) { 1989 return (0); 1990 } 1991 1992 1993 /* debug command handler: just print the parameters */ 1994 static int 1995 /* LINTED E_STATIC_UNUSED */ 1996 debug_print_params(flag_t *f, int argc, char **argv) 1997 { 1998 int i; 1999 #if 0 2000 char *leaktest = (char *)malloc(100); 2001 #endif 2002 2003 print_flags(f); 2004 2005 for (i = 0; i < argc; i++) { 2006 (void) printf("Argument %d: %s\n", i, argv[i]); 2007 } 2008 2009 (void) fflush(stdout); 2010 return (0); 2011 } 2012 2013 /* 2014 * Return a pointer after a given prefix. If there is no such prefix, 2015 * return NULL 2016 */ 2017 static char * 2018 get_root(char *string, char *typestring) { 2019 if (strcasecmp_no0(string, typestring) != 0) 2020 return (NULL); 2021 return (string + strlen(typestring)); 2022 } 2023 2024 /* 2025 * From name_mapping_t, asseble a string containing identity of the 2026 * given type. 2027 */ 2028 static int 2029 nm2type(name_mapping_t *nm, int type, char **to) { 2030 switch (type) { 2031 case TYPE_SID: 2032 if (nm->sidprefix == NULL) 2033 return (-1); 2034 *to = sid_format(nm->sidprefix, nm->rid); 2035 return (0); 2036 case TYPE_WN: 2037 return (nm2winqn(nm, to)); 2038 case TYPE_UID: 2039 case TYPE_GID: 2040 case TYPE_PID: 2041 *to = pid_format(nm->pid, nm->is_user); 2042 if (*to == NULL) 2043 return (-1); 2044 else 2045 return (0); 2046 case TYPE_UN: 2047 return (nm2unixname(nm, to)); 2048 default: 2049 /* This can never happen: */ 2050 (void) fprintf(stderr, 2051 gettext("Internal error: invalid name type.\n")); 2052 return (-1); 2053 } 2054 /* never reached */ 2055 } 2056 2057 /* show command handler */ 2058 static int 2059 do_show_mapping(flag_t *f, int argc, char **argv) 2060 { 2061 idmap_stat stat = 0; 2062 int flag; 2063 idmap_stat map_stat = 0; 2064 int type_from; 2065 int type_to; 2066 char *root; 2067 name_mapping_t *nm = NULL; 2068 char *fromname; 2069 char *toname; 2070 2071 if (argc == 0) { 2072 (void) fprintf(stderr, 2073 gettext("No identity given\n")); 2074 return (-1); 2075 } else if (argc > 2) { 2076 (void) fprintf(stderr, 2077 gettext("Too many arguments.\n")); 2078 return (-1); 2079 } 2080 2081 flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC; 2082 2083 if (init_command()) 2084 return (-1); 2085 2086 nm = name_mapping_init(); 2087 if (nm == NULL) 2088 goto cleanup; 2089 2090 /* First, determine type_from: */ 2091 if ((root = get_root(argv[0], ID_UID ":")) != NULL) 2092 type_from = TYPE_UID; 2093 else if ((root = get_root(argv[0], ID_GID ":")) != NULL) 2094 type_from = TYPE_GID; 2095 else if ((root = get_root(argv[0], ID_SID ":")) != NULL) 2096 type_from = TYPE_SID; 2097 else if (name2parts(argv[0], nm) > 0) { 2098 if (nm->unixname != NULL) 2099 type_from = TYPE_UN; 2100 else 2101 type_from = TYPE_WN; 2102 } else { 2103 (void) fprintf(stderr, 2104 gettext("Invalid type.\n")); 2105 stat = IDMAP_ERR_ARG; 2106 goto cleanup; 2107 } 2108 2109 /* Second, determine type_to: */ 2110 if (argc < 2) { 2111 type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID; 2112 if (type_from & IS_NAME) 2113 type_to |= IS_NAME; 2114 } else if (strcasecmp(argv[1], ID_UID) == 0) 2115 type_to = TYPE_UID; 2116 else if (strcasecmp(argv[1], ID_GID) == 0) 2117 type_to = TYPE_GID; 2118 else if (strcasecmp(argv[1], ID_SID) == 0) 2119 type_to = TYPE_SID; 2120 else if (strcmp(argv[1], ID_UNIXNAME) == 0) 2121 type_to = TYPE_UN; 2122 else if (strcmp(argv[1], ID_WINNAME) == 0) 2123 type_to = TYPE_WN; 2124 else { 2125 (void) fprintf(stderr, 2126 gettext("Ivnalid target type.\n")); 2127 stat = IDMAP_ERR_ARG; 2128 goto cleanup; 2129 } 2130 2131 /* Are both arguments the same OS side? */ 2132 if (!(type_from & IS_WIN ^ type_to & IS_WIN)) { 2133 (void) fprintf(stderr, 2134 gettext("Direction ambiguous.\n")); 2135 stat = IDMAP_ERR_ARG; 2136 goto cleanup; 2137 } 2138 2139 if (type_from == TYPE_SID) { 2140 if (!sid_convert(root, &nm->sidprefix, &nm->rid)) { 2141 stat = IDMAP_ERR_ARG; 2142 goto cleanup; 2143 } 2144 } else if (type_from == TYPE_UID || type_from == TYPE_GID) { 2145 if (!pid_convert(root, &nm->pid, type_from)) { 2146 stat = IDMAP_ERR_ARG; 2147 goto cleanup; 2148 } 2149 } 2150 2151 /* 2152 * We have two interfaces for retrieving the mappings: 2153 * idmap_get_sidbyuid & comp (the batch interface) and 2154 * idmap_get_w2u_mapping & comp. We want to use both of them, because 2155 * the former mimicks kernel interface better and the later offers the 2156 * string names. In the batch case, our batch has always size 1. 2157 */ 2158 if (type_from & IS_NAME || type_to & IS_NAME) { 2159 if (type_from & IS_WIN) { 2160 if (type_to == TYPE_UID) 2161 nm->is_user = I_YES; 2162 else if (type_to == TYPE_GID) 2163 nm->is_user = I_NO; 2164 2165 map_stat = idmap_get_w2u_mapping(handle, 2166 nm->sidprefix, 2167 &nm->rid, 2168 nm->winname, 2169 nm->windomain, 2170 flag, 2171 &nm->is_user, 2172 &nm->pid, 2173 &nm->unixname, 2174 &nm->direction); 2175 } else { 2176 if (type_from == TYPE_UID) 2177 nm->is_user = I_YES; 2178 else if (type_from == TYPE_GID) 2179 nm->is_user = I_NO; 2180 2181 map_stat = idmap_get_u2w_mapping(handle, 2182 &nm->pid, 2183 nm->unixname, 2184 flag, 2185 nm->is_user, 2186 &nm->sidprefix, 2187 &nm->rid, 2188 &nm->winname, 2189 &nm->windomain, 2190 &nm->direction); 2191 } 2192 2193 } else { 2194 /* batch handle */ 2195 idmap_get_handle_t *ghandle = NULL; 2196 /* To be passed to idmap_get_uidbysid */ 2197 gid_t gid = UNDEFINED_GID; 2198 /* To be passed to idmap_get_gidbysid */ 2199 uid_t uid = UNDEFINED_UID; 2200 2201 2202 /* Create an in-memory structure for all the batch: */ 2203 stat = idmap_get_create(handle, &ghandle); 2204 if (stat < 0) { 2205 (void) fprintf(stderr, 2206 gettext("Unable to create handle for communicating" 2207 " with idmapd(1M) (%s)\n"), 2208 idmap_stat2string(handle, stat)); 2209 idmap_get_destroy(ghandle); 2210 goto cleanup; 2211 } 2212 2213 /* Schedule the request: */ 2214 if (type_from == TYPE_SID && type_to == TYPE_UID) { 2215 stat = idmap_get_uidbysid(ghandle, 2216 nm->sidprefix, 2217 nm->rid, 2218 flag, 2219 &uid, 2220 &map_stat); 2221 nm->is_user = I_YES; 2222 } else if (type_from == TYPE_SID && type_to == TYPE_GID) { 2223 stat = idmap_get_gidbysid(ghandle, 2224 nm->sidprefix, 2225 nm->rid, 2226 flag, 2227 &gid, 2228 &map_stat); 2229 nm->is_user = I_NO; 2230 } else if (type_from == TYPE_SID && type_to == TYPE_PID) 2231 stat = idmap_get_pidbysid(ghandle, 2232 nm->sidprefix, 2233 nm->rid, 2234 flag, 2235 &nm->pid, 2236 &nm->is_user, 2237 &map_stat); 2238 else if (type_from == TYPE_UID && type_to == TYPE_SID) { 2239 stat = idmap_get_sidbyuid(ghandle, 2240 nm->pid, 2241 flag, 2242 &nm->sidprefix, 2243 &nm->rid, 2244 &map_stat); 2245 nm->is_user = I_YES; 2246 } else if (type_from == TYPE_GID && type_to == TYPE_SID) { 2247 stat = idmap_get_sidbygid(ghandle, 2248 (gid_t)nm->pid, 2249 flag, 2250 &nm->sidprefix, 2251 &nm->rid, 2252 &map_stat); 2253 nm->is_user = I_NO; 2254 } else { 2255 /* This can never happen: */ 2256 (void) fprintf(stderr, 2257 gettext("Internal error in show.\n")); 2258 exit(1); 2259 } 2260 2261 if (stat < 0) { 2262 (void) fprintf(stderr, 2263 gettext("Request for %.3s not sent (%s)\n"), 2264 argv[0], idmap_stat2string(handle, stat)); 2265 idmap_get_destroy(ghandle); 2266 goto cleanup; 2267 } 2268 2269 /* Send the batch to idmapd and obtain results: */ 2270 stat = idmap_get_mappings(ghandle); 2271 if (stat < 0) { 2272 (void) fprintf(stderr, 2273 gettext("Mappings not obtained because of" 2274 " RPC problem (%s)\n"), 2275 idmap_stat2string(handle, stat)); 2276 idmap_get_destroy(ghandle); 2277 goto cleanup; 2278 } 2279 2280 /* Destroy the batch handle: */ 2281 idmap_get_destroy(ghandle); 2282 2283 if (type_to == TYPE_UID) 2284 nm->pid = uid; 2285 else if (type_to == TYPE_GID) 2286 nm->pid = (uid_t)gid; 2287 2288 } 2289 2290 /* 2291 * If there was -c flag, we do output whatever we can even in 2292 * the case of error: 2293 */ 2294 if (map_stat < 0) { 2295 (void) fprintf(stderr, 2296 gettext("%s\n"), 2297 idmap_stat2string(handle, map_stat)); 2298 if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC) 2299 goto cleanup; 2300 } 2301 2302 2303 if (nm2type(nm, type_from, &fromname) < 0) 2304 goto cleanup; 2305 2306 if (nm2type(nm, type_to, &toname) < 0) { 2307 if (flag == 0) 2308 (void) printf("%s -> %s:%u\n", 2309 fromname, 2310 (type_from | type_to) & IS_GROUP ? ID_GID : ID_UID, 2311 UID_NOBODY); 2312 free(fromname); 2313 goto cleanup; 2314 } 2315 2316 (void) printf("%s -> %s\n", fromname, toname); 2317 free(fromname); 2318 free(toname); 2319 2320 cleanup: 2321 if (nm != NULL) 2322 name_mapping_fini(nm); 2323 fini_command(); 2324 return (stat < 0 || map_stat < 0 ? -1 : 0); 2325 } 2326 2327 /* main function. Returns 1 for error, 0 otherwise */ 2328 int 2329 main(int argc, char *argv[]) { 2330 int rc; 2331 2332 /* set locale and domain for internationalization */ 2333 (void) setlocale(LC_ALL, ""); 2334 (void) textdomain(TEXT_DOMAIN); 2335 2336 /* idmap_engine determines the batch_mode: */ 2337 rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t), 2338 commands, 2339 argc - 1, 2340 argv + 1, 2341 &batch_mode); 2342 2343 if (rc < 0) { 2344 (void) engine_fini(); 2345 if (rc == IDMAP_ENG_ERROR_SILENT) 2346 help(); 2347 return (1); 2348 } 2349 2350 udt_used = 0; 2351 if (batch_mode) { 2352 if (init_udt_batch() < 0) 2353 return (1); 2354 } 2355 2356 rc = run_engine(argc - 1, argv + 1); 2357 2358 if (batch_mode) { 2359 batch_mode = 0; 2360 fini_udt_command(rc == 0 ? 1 : 0); 2361 } 2362 2363 (void) engine_fini(); 2364 return (rc == 0 ? 0 : 1); 2365 } 2366