xref: /illumos-gate/usr/src/cmd/idmap/idmap/idmap.c (revision 8521309553394e26d53db0f665ff09b3b7ef7ea0)
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 2008 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 <sys/varargs.h>
35 #include "idmap_engine.h"
36 #include "idmap_priv.h"
37 
38 /* Initialization values for pids/rids: */
39 
40 #define	UNDEFINED_UID (uid_t)-1
41 #define	UNDEFINED_GID (gid_t)-1
42 #define	UNDEFINED_RID (idmap_rid_t)-1;
43 
44 /* is_user values */
45 
46 #define	I_YES 1
47 #define	I_NO 0
48 #define	I_UNKNOWN -1
49 
50 /*
51  * used in do_show for the type of argument, which can be winname,
52  * unixname, uid, gid, sid or not given at all:
53  */
54 
55 #define	TYPE_SID	0x010	/* sid */
56 #define	TYPE_USID	0x011	/* usid */
57 #define	TYPE_GSID	0x012	/* gsid */
58 #define	TYPE_WN		0x110	/* winname */
59 #define	TYPE_WU		0x111	/* winuser */
60 #define	TYPE_WG		0x112	/* wingroup */
61 #define	TYPE_UID	0x001	/* uid */
62 #define	TYPE_GID	0x002	/* gid */
63 #define	TYPE_PID	0x000	/* pid */
64 #define	TYPE_UN		0x100	/* unixname */
65 #define	TYPE_UU		0x101	/* unixuser */
66 #define	TYPE_UG		0x102	/* unixgroup */
67 
68 #define	IS_WIN		0x010	/* mask for the windows types */
69 #define	IS_NAME		0x100	/* mask for string name types */
70 #define	IS_USER		0x001	/* mask for user types */
71 #define	IS_GROUP	0x002	/* mask for group types */
72 
73 
74 /* Identity type strings */
75 
76 #define	ID_WINNAME	"winname"
77 #define	ID_UNIXNAME	"unixname"
78 #define	ID_UNIXUSER	"unixuser"
79 #define	ID_UNIXGROUP	"unixgroup"
80 #define	ID_WINUSER	"winuser"
81 #define	ID_WINGROUP	"wingroup"
82 #define	ID_USID	"usid"
83 #define	ID_GSID	"gsid"
84 #define	ID_SID	"sid"
85 #define	ID_UID	"uid"
86 #define	ID_GID	"gid"
87 
88 #define	INHIBITED(str)	(str == NULL || *str == 0 || strcmp(str, "\"\"") == 0)
89 
90 typedef struct {
91 	char *identity;
92 	int code;
93 } id_code_t;
94 
95 id_code_t identity2code[] = {
96 	{ID_WINNAME,	TYPE_WN},
97 	{ID_UNIXNAME,	TYPE_UN},
98 	{ID_UNIXUSER,	TYPE_UU},
99 	{ID_UNIXGROUP,	TYPE_UG},
100 	{ID_WINUSER,	TYPE_WU},
101 	{ID_WINGROUP,	TYPE_WG},
102 	{ID_USID,	TYPE_USID},
103 	{ID_GSID,	TYPE_GSID},
104 	{ID_SID,	TYPE_SID},
105 	{ID_UID,	TYPE_UID},
106 	{ID_GID,	TYPE_GID}
107 };
108 
109 
110 /* Flags */
111 
112 #define	f_FLAG	'f'
113 #define	t_FLAG	't'
114 #define	d_FLAG	'd'
115 #define	F_FLAG	'F'
116 #define	a_FLAG	'a'
117 #define	n_FLAG	'n'
118 #define	c_FLAG	'c'
119 #define	v_FLAG	'v'
120 
121 
122 /* used in the function do_import */
123 #define	MAX_INPUT_LINE_SZ 2047
124 
125 
126 typedef struct {
127 	int is_user;
128 	int is_wuser;
129 	int direction;
130 	boolean_t is_nt4;
131 	char *unixname;
132 	char *winname;
133 	char *windomain;
134 	char *sidprefix;
135 	idmap_rid_t rid;
136 	uid_t pid;
137 } name_mapping_t;
138 
139 /*
140  * Formats of the output:
141  *
142  * Idmap reads/prints mappings in several formats: ordinary mappings,
143  * name mappings in Samba username map format (smbusers), Netapp
144  * usermap.cfg.
145  *
146  * DEFAULT_FORMAT are in fact the idmap subcommands suitable for
147  * piping to idmap standart input. For example
148  * add -d winuser:bob@foo.com unixuser:fred
149  * add -d winuser:bob2bar.com unixuser:fred
150  *
151  * SMBUSERS is the format of Samba username map (smbusers). For full
152  * documentation, search for "username map" in smb.conf manpage.
153  * The format is for example
154  *    fred = bob@foo.com bob2@bar.com
155  *
156  * USERMAP_CFG is the format of Netapp usermap.cfg file. Search
157  * http://www.netapp.com/ for more documentation. IP qualifiers are not
158  * supported.
159  * The format is for example
160  *    bob@foo.com => fred
161  *    "Bob With Spaces"@bar.com => fred  #comment
162  *
163  * The previous formats were for name rules. MAPPING_NAME and
164  * MAPPING_ID are for the actual mappings, as seen in show/dump
165  * commands. MAPPING_NAME prefers the string names of the user over
166  * their numerical identificators. MAPPING_ID prints just the
167  * identificators.
168  * Example of the MAPPING_NAME:
169  *   winname:bob@foo.com -> unixname:fred
170  *
171  * Example of the MAPPING_ID:
172  *   sid:S-1-2-3-4 -> uid:5678
173  */
174 
175 typedef enum {
176 	UNDEFINED_FORMAT = -1,
177 	DEFAULT_FORMAT = 0,
178 	MAPPING_ID,
179 	MAPPING_NAME,
180 	USERMAP_CFG,
181 	SMBUSERS
182 } format_t;
183 
184 
185 typedef struct {
186 	format_t format;
187 	FILE *file;
188 	name_mapping_t *last;
189 } print_handle_t;
190 
191 /*
192  * idmap_api batch related variables:
193  *
194  * idmap can operate in two modes. It the batch mode, the idmap_api
195  * batch is committed at the end of a batch of several
196  * commands. At the end of input file, typically. This mode is used
197  * for processing input from a file.
198  *  In the non-batch mode, each command is committed immediately. This
199  * mode is used for tty input.
200  */
201 
202 /* Are we in the batch mode? */
203 static int batch_mode = 0;
204 
205 /* Self describing stricture for positions */
206 struct pos_sds {
207 	int size;
208 	int last;
209 	cmd_pos_t *pos[1];
210 };
211 
212 static struct pos_sds *positions;
213 
214 /* Handles for idmap_api batch */
215 static idmap_handle_t *handle = NULL;
216 static idmap_udt_handle_t *udt = NULL;
217 
218 /* Do we need to commit the udt batch at the end? */
219 static int udt_used;
220 
221 /* Command handlers */
222 
223 static int do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
224 static int do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
225 static int do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
226 static int do_list_name_mappings(flag_t *f, int argc, char **argv,
227     cmd_pos_t *pos);
228 static int do_add_name_mapping(flag_t *f, int argc, char **argv,
229     cmd_pos_t *pos);
230 static int do_remove_name_mapping(flag_t *f, int argc, char **argv,
231     cmd_pos_t *pos);
232 static int do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
233 static int do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
234 static int do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
235 
236 /* Command names and their hanlers to be passed to idmap_engine */
237 
238 static cmd_ops_t commands[] = {
239 	{
240 		"show",
241 		"c(create)v(verbose)",
242 		do_show_mapping
243 	},
244 	{
245 		"dump",
246 		"n(names)v(verbose)",
247 		do_dump
248 	},
249 	{
250 		"import",
251 		"F(flush)f:(file)",
252 		do_import
253 	},
254 	{
255 		"export",
256 		"f:(file)",
257 		do_export
258 	},
259 	{
260 		"list",
261 		"",
262 		do_list_name_mappings
263 	},
264 	{
265 		"add",
266 		"d(directional)",
267 		do_add_name_mapping
268 	},
269 	{
270 		"remove",
271 		"a(all)t(to)f(from)d(directional)",
272 		do_remove_name_mapping
273 	},
274 	{
275 		"exit",
276 		"",
277 		do_exit
278 	},
279 	{
280 		"help",
281 		"",
282 		do_help
283 	}
284 };
285 
286 /* Print error message, possibly with a position */
287 /* printflike */
288 static void
289 print_error(cmd_pos_t *pos, const char *format, ...)
290 {
291 	size_t length;
292 
293 	va_list ap;
294 
295 	va_start(ap, format);
296 
297 	if (pos != NULL) {
298 		length = strlen(pos->line);
299 
300 		/* Skip newlines etc at the end: */
301 		while (length > 0 && isspace(pos->line[length - 1]))
302 			length--;
303 
304 		(void) fprintf(stderr,
305 		    gettext("Error at line %d: %.*s\n"),
306 		    pos->linenum,
307 		    length,
308 		    pos->line);
309 	}
310 	(void) vfprintf(stderr, format, ap);
311 
312 	va_end(ap);
313 }
314 
315 /* Inits positions sds. 0 means everything went OK, -1 for errors */
316 static int
317 init_positions()
318 {
319 	int init_size = 32; /* Initial size of the positions array */
320 
321 	positions = (struct pos_sds *) malloc(sizeof (struct pos_sds) +
322 	    (init_size - 1) * sizeof (cmd_pos_t *));
323 
324 	if (positions == NULL) {
325 		print_error(NULL, gettext("Not enough memory.\n"));
326 		return (-1);
327 	}
328 
329 	positions->size = init_size;
330 	positions->last = 0;
331 	return (0);
332 }
333 
334 /* Free the positions array */
335 static void
336 fini_positions()
337 {
338 	int i;
339 	for (i = 0; i < positions->last; i++) {
340 		if (positions->pos[i] == NULL)
341 			continue;
342 		free(positions->pos[i]->line);
343 		free(positions->pos[i]);
344 	}
345 	free(positions);
346 
347 	positions = NULL;
348 }
349 
350 /*
351  * Add another position to the positions array. 0 means everything
352  * went OK, -1 for errors
353  */
354 static int
355 positions_add(cmd_pos_t *pos)
356 {
357 	if (positions->last >= positions->size) {
358 		positions->size *= 2;
359 		positions = (struct pos_sds *)realloc(positions,
360 		    sizeof (struct pos_sds) +
361 		    (positions->size - 1) * sizeof (cmd_pos_t *));
362 		if (positions == NULL)
363 			goto nomemory;
364 	}
365 
366 	if (pos == NULL)
367 		positions->pos[positions->last] = NULL;
368 	else {
369 		positions->pos[positions->last] = (cmd_pos_t *)calloc(1,
370 		    sizeof (cmd_pos_t));
371 		if (positions->pos[positions->last] == NULL)
372 			goto nomemory;
373 
374 		*positions->pos[positions->last] = *pos;
375 		positions->pos[positions->last]->line = strdup(pos->line);
376 		if (positions->pos[positions->last]->line == NULL)
377 			goto nomemory;
378 	}
379 
380 	positions->last++;
381 	return (0);
382 
383 nomemory:
384 	print_error(NULL, gettext("Not enough memory.\n"));
385 	return (-1);
386 }
387 
388 
389 
390 
391 /*
392  * Compare two strings just like strcmp, but stop before the end of
393  * the s2
394  */
395 static int
396 strcmp_no0(const char *s1, const char *s2)
397 {
398 	return (strncmp(s1, s2, strlen(s2)));
399 }
400 
401 /* Print help message */
402 static void
403 help()
404 {
405 	(void) fprintf(stderr,
406 	    "idmap\n"
407 	    "idmap -f command-file\n"
408 	    "idmap show [-c] [-v] identity [targettype]\n"
409 	    "idmap dump [-n] [-v]\n"
410 	    "idmap add [-d] name1 name2\n"
411 	    "idmap remove -a\n"
412 	    "idmap remove [-f|-t] name\n"
413 	    "idmap remove [-d] name1 name2\n"
414 	    "idmap list\n"
415 	    "idmap import [-F] [-f file] format\n"
416 	    "idmap export [-f file] format\n"
417 	    "idmap help\n");
418 }
419 
420 /* The handler for the "help" command. */
421 static int
422 /* LINTED E_FUNC_ARG_UNUSED */
423 do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
424 {
425 	help();
426 	return (0);
427 }
428 
429 /* Initialization of the idmap api batch */
430 static int
431 init_batch()
432 {
433 	idmap_stat stat;
434 
435 	stat = idmap_init(&handle);
436 	if (stat < 0) {
437 		print_error(NULL,
438 		    gettext("Connection not established (%s)\n"),
439 		    idmap_stat2string(NULL, stat));
440 		return (-1);
441 	}
442 
443 	return (0);
444 }
445 
446 /* Initialization common to all commands */
447 static int
448 init_command()
449 {
450 	if (batch_mode)
451 		return (0);
452 
453 	return (init_batch());
454 }
455 
456 /* Finalization common to all commands */
457 static void
458 fini_command()
459 {
460 	if (batch_mode)
461 		return;
462 	(void) idmap_fini(handle);
463 	handle = NULL;
464 }
465 
466 /* Initialization of the commands which perform write operations  */
467 static int
468 init_udt_batch()
469 {
470 	idmap_stat stat;
471 
472 	if (init_batch())
473 		return (-1);
474 
475 	stat = idmap_udt_create(handle, &udt);
476 	if (stat < 0) {
477 		print_error(NULL,
478 		    gettext("Error initiating transaction (%s)"),
479 		    idmap_stat2string(handle, stat));
480 		return (-1);
481 	}
482 
483 	if (init_positions() < 0)
484 		return (-1);
485 
486 	return (0);
487 }
488 
489 
490 /* Finalization of the write commands  */
491 static int
492 init_udt_command()
493 {
494 	udt_used = 1;
495 	if (batch_mode)
496 		return (0);
497 
498 	return (init_udt_batch());
499 }
500 
501 
502 /* If everythings is OK, send the udt batch to idmapd  */
503 static int
504 fini_udt_command(int ok, cmd_pos_t *pos)
505 {
506 	int rc = 0;
507 	int64_t failpos;
508 	idmap_stat stat, stat1;
509 	cmd_pos_t *reported_pos;
510 
511 	if (batch_mode)
512 		return (0);
513 	if (udt == NULL) {
514 		print_error(pos,
515 		    gettext("Internal error: uninitiated batch.\n"));
516 		return (-1);
517 	}
518 
519 	if (ok && udt_used) {
520 		stat = idmap_udt_commit(udt);
521 		if (stat == IDMAP_SUCCESS)
522 			goto out;
523 
524 		rc = -1;
525 
526 		stat1 = idmap_udt_get_error_index(udt, &failpos);
527 		if (stat1 != IDMAP_SUCCESS) {
528 			print_error(NULL,
529 			    gettext("Error diagnosing transaction (%s)\n"),
530 			    idmap_stat2string(handle, stat1));
531 			goto out;
532 		}
533 
534 
535 		if (failpos < 0)
536 			reported_pos = pos;
537 		else
538 			reported_pos = positions->pos[failpos];
539 
540 		print_error(reported_pos,
541 		    gettext("Error commiting transaction (%s)\n"),
542 		    idmap_stat2string(handle, stat));
543 	}
544 
545 out:
546 	idmap_udt_destroy(udt);
547 	udt = NULL;
548 	udt_used = 0;
549 	fini_command();
550 	fini_positions();
551 	return (rc);
552 }
553 
554 /* Convert numeric expression of the direction to it's string form */
555 static char *
556 direction2string(int direction)
557 {
558 	switch (direction) {
559 	case IDMAP_DIRECTION_BI:
560 		return ("==");
561 	case IDMAP_DIRECTION_W2U:
562 		return ("=>");
563 	case IDMAP_DIRECTION_U2W:
564 		return ("<=");
565 	default:
566 		/* This can never happen: */
567 		print_error(NULL,
568 		    gettext("Internal error: invalid direction.\n"));
569 		return ("");
570 	}
571 	/* never reached */
572 }
573 
574 /*
575  * Returns 1 if c is a shell-meta-character requiring quoting, 0
576  * otherwise.
577  *
578  * We don't quote '*' and ':' because they cannot do any harm
579  * a) they have no meaning to idmap_engine b) even ifsomebody copy &
580  * paste idmap output to a shell commandline, there is the identity
581  * type string in front of them. On the other hand, '*' and ':' are
582  * everywhere.
583  */
584 static int
585 is_shell_special(char c)
586 {
587 	if (isspace(c))
588 		return (1);
589 
590 	if (strchr("&^{}#;'\"\\`!$()[]><|~", c) != NULL)
591 		return (1);
592 
593 	return (0);
594 }
595 
596 /*
597  * Returns 1 if c is a shell-meta-character requiring quoting even
598  * inside double quotes, 0 otherwise. It means \, " and $ .
599  *
600  * This set of characters is a subset of those in is_shell_special().
601  */
602 static int
603 is_dq_special(char c)
604 {
605 	if (strchr("\\\"$", c) != NULL)
606 		return (1);
607 	return (0);
608 }
609 
610 
611 
612 
613 /*
614  * Quote any shell meta-characters in the given string.  If 'quote' is
615  * true then use double-quotes to quote the whole string, else use
616  * back-slash to quote each individual meta-character.
617  *
618  * The resulting string is placed in *res.  Callers must free *res if the
619  * return value isn't 0 (even if the given string had no meta-chars).
620  * If there are any errors this returns -1, else 0.
621  */
622 static int
623 shell_app(char **res, char *string, int quote)
624 {
625 	int i, j;
626 	uint_t noss = 0; /* Number Of Shell Special chars in the input */
627 	uint_t noqb = 0; /* Number Of Quotes and Backslahes in the input */
628 	char *out;
629 	size_t len_orig = strlen(string);
630 	size_t len;
631 
632 	if (INHIBITED(string)) {
633 		out = strdup("\"\"");
634 		if (out == NULL) {
635 			print_error(NULL, gettext("Not enough memory.\n"));
636 			return (-1);
637 		}
638 		*res = out;
639 		return (0);
640 	}
641 
642 	/* First, let us count how many characters we need to quote: */
643 	for (i = 0; i < len_orig; i++) {
644 		if (is_shell_special(string[i])) {
645 			noss++;
646 			if (is_dq_special(string[i]))
647 				noqb++;
648 		}
649 
650 	}
651 
652 	/* Do we need to quote at all? */
653 	if (noss == 0) {
654 		out = strdup(string);
655 		if (out == NULL) {
656 			print_error(NULL, gettext("Not enough memory.\n"));
657 			return (-1);
658 		}
659 		*res = out;
660 		return (0);
661 	}
662 
663 	/* What is the length of the result? */
664 	if (quote)
665 		len = strlen(string) + 2 + noqb + 1; /* 2 for quotation marks */
666 	else
667 		len = strlen(string) + noss + 1;
668 
669 	out = (char *)malloc(len);
670 	if (out == NULL) {
671 		print_error(NULL, gettext("Not enough memory.\n"));
672 		return (-1);
673 	}
674 
675 	j = 0;
676 	if (quote)
677 		out[j++] = '"';
678 
679 	for (i = 0; i < len_orig; i++) {
680 		/* Quote the dangerous chars by a backslash */
681 		if (quote && is_dq_special(string[i]) ||
682 		    (!quote && is_shell_special(string[i]))) {
683 			out[j++] = '\\';
684 		}
685 		out[j++] = string[i];
686 	}
687 
688 	if (quote)
689 		out[j++] = '"';
690 
691 	out[j] = '\0';
692 	*res = out;
693 	return (0);
694 }
695 
696 /* Assemble string form sid */
697 static char *
698 sid_format(name_mapping_t *nm)
699 {
700 	char *to;
701 	size_t len;
702 	char *typestring;
703 
704 	switch (nm->is_wuser) {
705 	case I_YES:
706 		typestring = ID_USID;
707 		break;
708 	case I_NO:
709 		typestring = ID_GSID;
710 		break;
711 	default:
712 		typestring = ID_SID;
713 		break;
714 	}
715 
716 	/* 'usid:' + sidprefix + '-' + rid + '\0' */
717 	len = strlen(nm->sidprefix) + 7 + 3 * sizeof (nm->rid);
718 	to = (char *)malloc(len);
719 	if (to == NULL)
720 		return (NULL);
721 
722 	(void) snprintf(to, len, "%s:%s-%u", typestring, nm->sidprefix,
723 	    nm->rid);
724 	return (to);
725 }
726 
727 /* Assemble string form uid or gid */
728 static char *
729 pid_format(uid_t from, int is_user)
730 {
731 	char *to;
732 	size_t len;
733 
734 	/* ID_UID ":" + uid + '\0' */
735 	len = 5 + 3 * sizeof (uid_t);
736 	to = (char *)malloc(len);
737 	if (to == NULL)
738 		return (NULL);
739 
740 	(void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from);
741 	return (to);
742 }
743 
744 /* Assemble winname, e.g. "winuser:bob@foo.sun.com", from name_mapping_t */
745 static int
746 nm2winqn(name_mapping_t *nm, char **winqn)
747 {
748 	char *out;
749 	size_t length = 0;
750 	int is_domain = 1;
751 	char *prefix;
752 
753 	/* Sometimes there are no text names. Return a sid, then. */
754 	if (nm->winname == NULL && nm->sidprefix != NULL) {
755 		*winqn = sid_format(nm);
756 		return (0);
757 	}
758 
759 	switch (nm->is_wuser) {
760 	case I_YES:
761 		prefix = ID_WINUSER ":";
762 		break;
763 	case I_NO:
764 		prefix = ID_WINGROUP ":";
765 		break;
766 	case I_UNKNOWN:
767 		prefix = ID_WINNAME ":";
768 		break;
769 
770 	}
771 
772 	length = strlen(prefix);
773 
774 	if (nm->winname != NULL)
775 		length += strlen(nm->winname);
776 
777 	/* Windomain is not mandatory: */
778 	if (nm->windomain == NULL || INHIBITED(nm->winname))
779 		is_domain = 0;
780 	else
781 		length += strlen(nm->windomain) + 1;
782 
783 	out = (char *)malloc(length + 1);
784 	if (out == NULL) {
785 		print_error(NULL,
786 		    gettext("Not enough memory.\n"));
787 		return (-1);
788 	}
789 
790 	(void) strcpy(out, prefix);
791 
792 	/* LINTED E_NOP_IF_STMT */
793 	if (nm->winname == NULL)
794 		;
795 	else if (!is_domain)
796 		(void) strcat(out, nm->winname);
797 	else if (nm->is_nt4) {
798 		(void) strcat(out, nm->windomain);
799 		(void) strcat(out, "\\");
800 		(void) strcat(out, nm->winname);
801 	} else {
802 		(void) strcat(out, nm->winname);
803 		(void) strcat(out, "@");
804 		(void) strcat(out, nm->windomain);
805 	}
806 
807 	*winqn = out;
808 	return (0);
809 }
810 
811 /*
812  * Assemble a text unixname, e.g. unixuser:fred. Use only for
813  * mapping, not namerules - there an empty name means inhibited
814  * mappings, while here pid is printed if there is no name.
815  */
816 static
817 int
818 nm2unixname(name_mapping_t *nm, char **unixname)
819 {
820 	size_t length = 0;
821 	char *out, *it, *prefix;
822 
823 	/* Sometimes there is no name, just pid: */
824 	if (nm->unixname == NULL) {
825 		if (nm->pid == UNDEFINED_UID)
826 			return (-1);
827 
828 		*unixname = pid_format(nm->pid, nm->is_user);
829 		return (0);
830 	}
831 
832 	if (shell_app(&it, nm->unixname, 0))
833 		return (-1);
834 
835 
836 	switch (nm->is_user) {
837 	case I_YES:
838 		prefix = ID_UNIXUSER ":";
839 		break;
840 	case I_NO:
841 		prefix = ID_UNIXGROUP ":";
842 		break;
843 	case I_UNKNOWN:
844 		prefix = ID_UNIXNAME ":";
845 		break;
846 
847 	}
848 
849 	length = strlen(prefix) + strlen(it);
850 
851 	out = (char *)malloc(length + 1);
852 	if (out == NULL) {
853 		print_error(NULL,
854 		    gettext("Not enough memory.\n"));
855 		free(it);
856 		return (-1);
857 	}
858 
859 	(void) strcpy(out, prefix);
860 	(void) strcat(out, it);
861 	free(it);
862 
863 	*unixname = out;
864 	return (0);
865 }
866 
867 /* Allocate a new name_mapping_t and initialize the values. */
868 static name_mapping_t *
869 name_mapping_init()
870 {
871 	name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t));
872 	if (nm == NULL) {
873 		print_error(NULL, gettext("Not enough memory.\n"));
874 		return (NULL);
875 	}
876 	nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL;
877 	nm->rid = UNDEFINED_RID;
878 	nm->is_nt4 = B_FALSE;
879 	nm->is_user = I_UNKNOWN;
880 	nm->is_wuser = I_UNKNOWN;
881 	nm->direction = IDMAP_DIRECTION_UNDEF;
882 	nm->pid = UNDEFINED_UID;
883 	return (nm);
884 }
885 
886 /* Free name_mapping_t */
887 static void
888 name_mapping_fini(name_mapping_t *nm)
889 {
890 
891 	free(nm->winname);
892 	free(nm->windomain);
893 	free(nm->unixname);
894 	free(nm->sidprefix);
895 
896 	free(nm);
897 }
898 
899 static int
900 name_mapping_cpy(name_mapping_t *to, name_mapping_t *from)
901 {
902 	free(to->winname);
903 	free(to->windomain);
904 	free(to->unixname);
905 	free(to->sidprefix);
906 
907 	(void) memcpy(to, from, sizeof (name_mapping_t));
908 	to->winname = to->windomain = to->unixname = to->sidprefix = NULL;
909 
910 	if (from->winname != NULL) {
911 		to->winname = strdup(from->winname);
912 		if (to->winname == NULL) {
913 			print_error(NULL, gettext("Not enough memory.\n"));
914 			return (-1);
915 		}
916 	}
917 
918 	if (from->windomain != NULL) {
919 		to->windomain = strdup(from->windomain);
920 		if (to->windomain == NULL)  {
921 			print_error(NULL, gettext("Not enough memory.\n"));
922 			return (-1);
923 		}
924 	}
925 
926 	if (from->unixname != NULL) {
927 		to->unixname = strdup(from->unixname);
928 		if (to->unixname == NULL)  {
929 			print_error(NULL, gettext("Not enough memory.\n"));
930 			return (-1);
931 		}
932 	}
933 
934 	if (from->sidprefix != NULL) {
935 		to->sidprefix = strdup(from->sidprefix);
936 		if (to->sidprefix == NULL)  {
937 			print_error(NULL, gettext("Not enough memory.\n"));
938 			return (-1);
939 		}
940 	}
941 
942 	return (0);
943 }
944 
945 static int
946 name_mapping_format(name_mapping_t *nm, char **out)
947 {
948 	char *winname = NULL;
949 	char *winname1 = NULL;
950 	char *unixname = NULL;
951 	int maxlen;
952 
953 	*out = NULL;
954 
955 	if (nm2winqn(nm, &winname1) < 0)
956 		return (-1);
957 
958 	if (shell_app(&winname, winname1, 1)) {
959 		free(winname1);
960 		return (-1);
961 	}
962 
963 	free(winname1);
964 
965 	if (nm2unixname(nm, &unixname)) {
966 		free(winname);
967 		return (-1);
968 	}
969 
970 	/* 10 is strlen("add -d\t\t\n") + 1 */
971 	maxlen = 10 + strlen(unixname) + strlen(winname);
972 
973 	*out = (char *)malloc(maxlen);
974 
975 	if (nm->direction == IDMAP_DIRECTION_U2W) {
976 		(void) snprintf(*out, maxlen, "add -d\t%s\t%s\n",
977 		    unixname, winname);
978 	} else {
979 		(void) snprintf(*out, maxlen, "add %s\t%s\t%s\n",
980 		    nm->direction == IDMAP_DIRECTION_BI? "" : "-d",
981 		    winname, unixname);
982 	}
983 	free(winname);
984 	free(unixname);
985 	return (0);
986 }
987 
988 /* Initialize print_mapping variables. Must be called before print_mapping */
989 static print_handle_t *
990 print_mapping_init(format_t f, FILE *fi)
991 {
992 	print_handle_t *out;
993 
994 	out = (print_handle_t *)malloc(sizeof (print_handle_t));
995 	if (out == NULL) {
996 		print_error(NULL, gettext("Not enough memory.\n"));
997 		return (NULL);
998 	}
999 
1000 	out->format = f;
1001 	out->file = fi;
1002 	out->last = name_mapping_init();
1003 
1004 	if (out->last == NULL)
1005 		return (NULL);
1006 
1007 	return (out);
1008 }
1009 
1010 /* Finalize print_mapping. */
1011 static int
1012 print_mapping_fini(print_handle_t *pnm)
1013 {
1014 	char *out = NULL;
1015 	int rc = 0;
1016 
1017 	switch (pnm->format) {
1018 	case SMBUSERS:
1019 		if (pnm->last->unixname != NULL) {
1020 			(void) fprintf(pnm->file, "\n");
1021 		}
1022 		break;
1023 	case DEFAULT_FORMAT:
1024 		if (pnm->last->unixname == NULL)
1025 			break;
1026 		rc = name_mapping_format(pnm->last, &out);
1027 		if (rc >= 0) {
1028 			(void) fprintf(pnm->file, "%s", out);
1029 			free(out);
1030 		}
1031 		break;
1032 	default:
1033 		;
1034 	}
1035 
1036 	name_mapping_fini(pnm->last);
1037 	free(pnm);
1038 
1039 	return (rc);
1040 }
1041 
1042 static char *
1043 usermap_cfg_string(char *in)
1044 {
1045 	int len;
1046 	char *out;
1047 
1048 	if (INHIBITED(in))
1049 		return (strdup("\"\""));
1050 
1051 	len = strlen(in);
1052 	if (len == strcspn(in, " \t#"))
1053 		return (strdup(in));
1054 
1055 	out = malloc(len + 3);
1056 	if (out == NULL)
1057 		return (NULL);
1058 
1059 	(void) snprintf(out, len + 3, "\"%s\"", in);
1060 	return (out);
1061 }
1062 
1063 /*
1064  * Compare two possibly NULL strings
1065  */
1066 static int
1067 strcmp_null(char *a, char *b)
1068 {
1069 	if (a == NULL && b == NULL)
1070 		return (0);
1071 	if (a == NULL)
1072 		return (-1);
1073 	if (b == NULL)
1074 		return (1);
1075 	return (strcmp(a, b));
1076 }
1077 
1078 /*
1079  * This prints both name rules and ordinary mappings, based on the pnm_format
1080  * set in print_mapping_init().
1081  */
1082 
1083 static int
1084 print_mapping(print_handle_t *pnm, name_mapping_t *nm)
1085 {
1086 	char *dirstring;
1087 	char *winname = NULL;
1088 	char *windomain = NULL;
1089 	char *unixname = NULL;
1090 	FILE *f = pnm->file;
1091 
1092 	switch (pnm->format) {
1093 	case MAPPING_NAME:
1094 		if (nm2winqn(nm, &winname) < 0)
1095 			return (-1);
1096 		if (nm2unixname(nm, &unixname) < 0) {
1097 			free(winname);
1098 			return (-1);
1099 		}
1100 	/* LINTED E_CASE_FALLTHRU */
1101 	case MAPPING_ID:
1102 		if (pnm->format == MAPPING_ID) {
1103 			if (nm->sidprefix == NULL) {
1104 				print_error(NULL,
1105 				    gettext("SID not given.\n"));
1106 				return (-1);
1107 			}
1108 			winname = sid_format(nm);
1109 			if (winname == NULL)
1110 				return (-1);
1111 			unixname = pid_format(nm->pid, nm->is_user);
1112 			if (unixname == NULL) {
1113 				free(winname);
1114 				return (-1);
1115 			}
1116 		}
1117 
1118 		dirstring = direction2string(nm->direction);
1119 
1120 		(void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring,
1121 		    unixname);
1122 
1123 		break;
1124 	case SMBUSERS:
1125 		if (nm->is_user != I_YES || nm->is_wuser != I_YES) {
1126 			print_error(NULL,
1127 			    gettext("Group rule: "));
1128 			f = stderr;
1129 		} else 	if (nm->direction == IDMAP_DIRECTION_U2W) {
1130 			print_error(NULL,
1131 			    gettext("Opposite direction of the mapping: "));
1132 			f = stderr;
1133 		} else if (INHIBITED(nm->winname) || INHIBITED(nm->unixname)) {
1134 			print_error(NULL, gettext("Inhibited rule: "));
1135 			f = stderr;
1136 		}
1137 
1138 		if (shell_app(&winname, nm->winname, 1))
1139 			return (-1);
1140 
1141 		unixname = INHIBITED(nm->unixname) ? "\"\"" : nm->unixname;
1142 
1143 		if (pnm->file != f) {
1144 			(void) fprintf(f, "%s=%s\n", unixname, winname);
1145 		} else if (pnm->last->unixname != NULL &&
1146 		    strcmp(pnm->last->unixname, unixname) == 0) {
1147 			(void) fprintf(f, " %s", winname);
1148 		} else {
1149 			if (pnm->last->unixname != NULL) {
1150 				(void) fprintf(f, "\n");
1151 				free(pnm->last->unixname);
1152 			}
1153 			pnm->last->unixname = strdup(unixname);
1154 			if (pnm->last->unixname == NULL) {
1155 				print_error(NULL,
1156 				    gettext("Not enough memory.\n"));
1157 			}
1158 
1159 			(void) fprintf(f, "%s=%s", unixname, winname);
1160 		}
1161 
1162 		unixname = NULL;
1163 		break;
1164 	case USERMAP_CFG:
1165 		if (nm->is_user != I_YES || nm->is_wuser != I_YES) {
1166 			print_error(NULL,
1167 			    gettext("Group rule: "));
1168 			f = stderr;
1169 		}
1170 
1171 		dirstring = direction2string(nm->direction);
1172 
1173 		if ((winname = usermap_cfg_string(nm->winname)) == NULL ||
1174 		    (unixname = usermap_cfg_string(nm->unixname)) == NULL ||
1175 		    (windomain = usermap_cfg_string(nm->windomain)) == NULL) {
1176 			print_error(NULL, gettext("Not enough memory.\n"));
1177 			free(winname);
1178 			free(unixname);
1179 			free(windomain);
1180 			return (-1);
1181 		}
1182 
1183 
1184 		if (nm->windomain == NULL) {
1185 			(void) fprintf(f, "%s\t%s\t%s\n",
1186 			    winname, dirstring, unixname);
1187 		} else
1188 			(void) fprintf(f, nm->is_nt4 ?
1189 			    "%s\\%s\t%s\t%s\n" :
1190 			    "%2$s@%1$s\t%3$s\t%4$s\n",
1191 			    windomain, winname, dirstring, unixname);
1192 
1193 		break;
1194 
1195 	/* This is a format for namerules */
1196 	case DEFAULT_FORMAT:
1197 		/*
1198 		 * If nm is the same as the last one except is_wuser, we combine
1199 		 * winuser & wingroup to winname
1200 		 */
1201 		if (nm->direction == pnm->last->direction &&
1202 		    nm->is_user == pnm->last->is_user &&
1203 
1204 		    strcmp_null(pnm->last->unixname, nm->unixname) == 0 &&
1205 		    strcmp_null(pnm->last->winname, nm->winname) == 0 &&
1206 		    strcmp_null(pnm->last->windomain, nm->windomain) == 0) {
1207 			pnm->last->is_wuser = I_UNKNOWN;
1208 		} else {
1209 			if (pnm->last->unixname != NULL ||
1210 			    pnm->last->winname != NULL) {
1211 				char *out = NULL;
1212 				if (name_mapping_format(pnm->last, &out) < 0)
1213 					return (-1);
1214 				(void) fprintf(f, "%s", out);
1215 				free(out);
1216 			}
1217 			if (name_mapping_cpy(pnm->last, nm) < 0)
1218 				return (-1);
1219 		}
1220 		break;
1221 	default:
1222 		/* This can never happen: */
1223 		print_error(NULL,
1224 		    gettext("Internal error: invalid print format.\n"));
1225 		return (-1);
1226 	}
1227 
1228 	free(winname);
1229 	free(unixname);
1230 	free(windomain);
1231 	return (0);
1232 }
1233 
1234 
1235 static
1236 void
1237 print_how(idmap_how *how)
1238 {
1239 	idmap_namerule	*rule;
1240 	name_mapping_t	nm;
1241 	char		*rule_text;
1242 
1243 	switch (how->map_type) {
1244 	case IDMAP_MAP_TYPE_DS_AD:
1245 		(void) printf(gettext("Method:\tAD Directory\n"));
1246 		(void) printf(gettext("DN:\t%s\n"),
1247 		    how->idmap_how_u.ad.dn);
1248 		(void) printf(gettext("Attribute:\t%s=%s\n"),
1249 		    how->idmap_how_u.ad.attr,
1250 		    how->idmap_how_u.ad.value);
1251 		break;
1252 
1253 	case IDMAP_MAP_TYPE_DS_NLDAP:
1254 		(void) printf(gettext("Method:\tNative LDAP Directory\n"));
1255 		(void) printf(gettext("DN:\t%s\n"),
1256 		    how->idmap_how_u.nldap.dn);
1257 		(void) printf(gettext("Attribute:\t%s=%s\n"),
1258 		    how->idmap_how_u.nldap.attr,
1259 		    how->idmap_how_u.nldap.value);
1260 		break;
1261 
1262 	case IDMAP_MAP_TYPE_RULE_BASED:
1263 		(void) printf(gettext("Method:\tName Rule\n"));
1264 		rule = &how->idmap_how_u.rule;
1265 		/*
1266 		 * The name rules as specified by the user can have a
1267 		 * "winname", "winuser" or "wingroup". "Winname" rules are
1268 		 * decomposed to a "winuser" and "wingroup" rules by idmap.
1269 		 * Currently is_wuser  is a boolean. Due to these reasons
1270 		 * the returned is_wuser does not represent the original rule.
1271 		 * It is therefore better set is_wuser to unknown.
1272 		 */
1273 		nm.is_user = rule->is_user;
1274 		nm.is_wuser = I_UNKNOWN;
1275 		nm.direction = rule->direction;
1276 		nm.winname = rule->winname;
1277 		nm.windomain = rule->windomain;
1278 		nm.unixname = rule->unixname;
1279 		nm.is_nt4 = rule->is_nt4;
1280 		if (name_mapping_format(&nm, &rule_text) == 0) {
1281 			(void) printf(gettext("Rule:\t%s"), rule_text);
1282 			free(rule_text);
1283 		}
1284 		break;
1285 
1286 	case IDMAP_MAP_TYPE_EPHEMERAL:
1287 		(void) printf(gettext("Method:\tEphemeral\n"));
1288 		break;
1289 
1290 	case IDMAP_MAP_TYPE_LOCAL_SID:
1291 		(void) printf(gettext("Method:\tLocal SID\n"));
1292 		break;
1293 
1294 	case IDMAP_MAP_TYPE_KNOWN_SID:
1295 		(void) printf(gettext("Method:\tWell-Known mapping\n"));
1296 		break;
1297 	}
1298 }
1299 
1300 
1301 
1302 
1303 
1304 static
1305 void
1306 print_info(idmap_info *info)
1307 {
1308 	if (info->how.map_type != IDMAP_MAP_TYPE_UNKNOWN) {
1309 		switch (info->src) {
1310 		case IDMAP_MAP_SRC_NEW:
1311 			(void) printf(gettext("Source:\tNew\n"));
1312 			break;
1313 
1314 		case IDMAP_MAP_SRC_CACHE:
1315 			(void) printf(gettext("Source:\tCache\n"));
1316 			break;
1317 
1318 		case IDMAP_MAP_SRC_HARD_CODED:
1319 			(void) printf(gettext("Source:\tHard Coded\n"));
1320 			break;
1321 
1322 		case IDMAP_MAP_SRC_ALGORITHMIC:
1323 			(void) printf(gettext("Source:\tAlgorithmic\n"));
1324 			break;
1325 		}
1326 		print_how(&info->how);
1327 	}
1328 }
1329 
1330 
1331 static
1332 void
1333 print_error_info(idmap_info *info)
1334 {
1335 	idmap_how	*how = &info->how;
1336 	idmap_namerule	*rule;
1337 	name_mapping_t	nm;
1338 	char		*rule_text;
1339 
1340 	switch (how->map_type) {
1341 	case IDMAP_MAP_TYPE_DS_AD:
1342 		(void) fprintf(stderr,
1343 		    gettext("Failed Method:\tAD Directory\n"));
1344 		(void) fprintf(stderr, gettext("DN:\t%s\n"),
1345 		    how->idmap_how_u.ad.dn);
1346 		(void) fprintf(stderr, gettext("Attribute:\t%s=%s\n"),
1347 		    how->idmap_how_u.ad.attr,
1348 		    how->idmap_how_u.ad.value);
1349 		break;
1350 
1351 	case IDMAP_MAP_TYPE_DS_NLDAP:
1352 		(void) fprintf(stderr,
1353 		    gettext("Failed Method:\tNative LDAP Directory\n"));
1354 		(void) fprintf(stderr, gettext("DN:\t%s\n"),
1355 		    how->idmap_how_u.nldap.dn);
1356 		(void) fprintf(stderr, gettext("Attribute:\t%s=%s\n"),
1357 		    how->idmap_how_u.nldap.attr,
1358 		    how->idmap_how_u.nldap.value);
1359 		break;
1360 
1361 	case IDMAP_MAP_TYPE_RULE_BASED:
1362 		(void) fprintf(stderr, gettext("Failed Method:\tName Rule\n"));
1363 		rule = &how->idmap_how_u.rule;
1364 		/*
1365 		 * The name rules as specified by the user can have a
1366 		 * "winname", "winuser" or "wingroup". "Winname" rules are
1367 		 * decomposed to a "winuser" and "wingroup" rules by idmap.
1368 		 * Currently is_wuser  is a boolean. Due to these reasons
1369 		 * the returned is_wuser does not represent the original rule.
1370 		 * It is therefore better to set is_wuser to unknown.
1371 		 */
1372 		nm.is_user = rule->is_user;
1373 		nm.is_wuser = I_UNKNOWN;
1374 		nm.direction = rule->direction;
1375 		nm.winname = rule->winname;
1376 		nm.windomain = rule->windomain;
1377 		nm.unixname = rule->unixname;
1378 		nm.is_nt4 = rule->is_nt4;
1379 		if (name_mapping_format(&nm, &rule_text) == 0) {
1380 			(void) fprintf(stderr, gettext("Rule:\t%s"), rule_text);
1381 			free(rule_text);
1382 		}
1383 		break;
1384 
1385 	case IDMAP_MAP_TYPE_EPHEMERAL:
1386 		(void) fprintf(stderr, gettext("Failed Method:\tEphemeral\n"));
1387 		break;
1388 
1389 	case IDMAP_MAP_TYPE_LOCAL_SID:
1390 		(void) fprintf(stderr, gettext("Failed Method:\tLocal SID\n"));
1391 		break;
1392 
1393 	case IDMAP_MAP_TYPE_KNOWN_SID:
1394 		(void) fprintf(stderr,
1395 		    gettext("Failed Method:\tWell-Known mapping\n"));
1396 		break;
1397 	}
1398 }
1399 
1400 
1401 
1402 /* dump command handler */
1403 static int
1404 /* LINTED E_FUNC_ARG_UNUSED */
1405 do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
1406 {
1407 	idmap_stat	stat;
1408 	idmap_iter_t	*ihandle;
1409 	int		rc = 0;
1410 	boolean_t	is_user;
1411 	boolean_t	is_wuser;
1412 	print_handle_t	*ph;
1413 	int		flag = 0;
1414 	idmap_info	info;
1415 
1416 	if (init_command())
1417 		return (-1);
1418 
1419 	ph = print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID,
1420 	    stdout);
1421 	if (ph == NULL)
1422 		return (-1);
1423 
1424 	if (f[v_FLAG] != NULL)
1425 		flag = IDMAP_REQ_FLG_MAPPING_INFO;
1426 
1427 	stat = idmap_iter_mappings(handle, &ihandle, flag);
1428 	if (stat < 0) {
1429 		print_error(pos,
1430 		    gettext("Iteration handle not obtained (%s)\n"),
1431 		    idmap_stat2string(handle, stat));
1432 		rc = -1;
1433 		goto cleanup;
1434 	}
1435 
1436 	do {
1437 		name_mapping_t *nm = name_mapping_init();
1438 		if (nm == NULL) {
1439 			rc = -1;
1440 			goto cleanup;
1441 		}
1442 
1443 		stat = idmap_iter_next_mapping(ihandle,
1444 		    &nm->sidprefix, &nm->rid, &nm->pid,
1445 		    &nm->winname, &nm->windomain,
1446 		    &nm->unixname, &is_user, &is_wuser,
1447 		    &nm->direction, &info);
1448 
1449 		nm->is_user = is_user ? I_YES : I_NO;
1450 		nm->is_wuser = is_wuser ? I_YES : I_NO;
1451 
1452 		if (stat >= 0) {
1453 			(void) print_mapping(ph, nm);
1454 			(void) print_how(&info.how);
1455 			idmap_info_free(&info);
1456 		}
1457 		name_mapping_fini(nm);
1458 
1459 	} while (stat > 0);
1460 
1461 	/* IDMAP_ERR_NOTFOUND indicates end of the list */
1462 	if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
1463 		print_error(pos,
1464 		    gettext("Error during iteration (%s)\n"),
1465 		    idmap_stat2string(handle, stat));
1466 		rc = -1;
1467 		goto cleanup;
1468 	}
1469 
1470 	idmap_iter_destroy(ihandle);
1471 
1472 cleanup:
1473 	(void) print_mapping_fini(ph);
1474 	fini_command();
1475 	return (rc);
1476 }
1477 
1478 /*
1479  * The same as strdup, but length chars is duplicated, no matter on
1480  * '\0'. The caller must guarantee "length" chars in "from".
1481  */
1482 static char *
1483 strndup(char *from, size_t length)
1484 {
1485 	char *out = (char *)malloc(length + 1);
1486 	if (out == NULL) {
1487 		print_error(NULL, gettext("Not enough memory\n"));
1488 		return (NULL);
1489 	}
1490 	(void) strncpy(out, from, length);
1491 	out[length] = '\0';
1492 	return (out);
1493 }
1494 
1495 /*
1496  * Convert pid from string to it's numerical representation. If it is
1497  * a valid string, i.e. number of a proper length, return 1. Otherwise
1498  * print an error message and return 0.
1499  */
1500 static int
1501 pid_convert(char *string, uid_t *number, int type, cmd_pos_t *pos)
1502 {
1503 	int i;
1504 	long long ll;
1505 	char *type_string;
1506 	size_t len = strlen(string);
1507 
1508 	if (type == TYPE_GID)
1509 		type_string = ID_GID;
1510 	else if (type == TYPE_UID)
1511 		type_string = ID_UID;
1512 	else
1513 		return (0);
1514 
1515 	for (i = 0; i < len; i++) {
1516 		if (!isdigit(string[i])) {
1517 			print_error(pos,
1518 			    gettext("\"%s\" is not a valid %s: the non-digit"
1519 			    " character '%c' found.\n"), string,
1520 			    type_string, string[i]);
1521 			return (0);
1522 		}
1523 	}
1524 
1525 	ll = atoll(string);
1526 
1527 	/* Isn't it too large? */
1528 	if (type == TYPE_UID && (uid_t)ll != ll ||
1529 	    type == TYPE_GID && (gid_t)ll != ll) {
1530 		print_error(pos,
1531 		    gettext("%llu: too large for a %s.\n"), ll,
1532 		    type_string);
1533 		return (0);
1534 	}
1535 
1536 	*number = (uid_t)ll;
1537 	return (1);
1538 }
1539 
1540 /*
1541  * Convert SID from string to prefix and rid. If it has a valid
1542  * format, i.e. S(\-\d+)+, return 1. Otherwise print an error
1543  * message and return 0.
1544  */
1545 static int
1546 sid_convert(char *from, char **prefix, idmap_rid_t *rid, cmd_pos_t *pos)
1547 {
1548 	int i, j;
1549 	char *cp;
1550 	char *ecp;
1551 	char *prefix_end;
1552 	u_longlong_t	a;
1553 	unsigned long	r;
1554 
1555 	if (strcmp_no0(from, "S-1-") != 0) {
1556 		print_error(pos,
1557 		    gettext("Invalid %s \"%s\": it doesn't start "
1558 		    "with \"%s\".\n"), ID_SID, from, "S-1-");
1559 		return (0);
1560 	}
1561 
1562 	if (strlen(from) <= strlen("S-1-")) {
1563 		print_error(pos,
1564 		    gettext("Invalid %s \"%s\": the authority and RID parts are"
1565 		    " missing.\n"),
1566 		    ID_SID, from);
1567 		return (0);
1568 	}
1569 
1570 	/* count '-'s */
1571 	for (j = 0, cp = strchr(from, '-');
1572 	    cp != NULL;
1573 	    j++, cp = strchr(cp + 1, '-')) {
1574 		/* can't end on a '-' */
1575 		if (*(cp + 1) == '\0') {
1576 			print_error(pos,
1577 			    gettext("Invalid %s \"%s\": '-' at the end.\n"),
1578 			    ID_SID, from);
1579 			return (0);
1580 		} else 	if (*(cp + 1) == '-') {
1581 			print_error(pos,
1582 			    gettext("Invalid %s \"%s\": double '-'.\n"),
1583 			    ID_SID, from);
1584 			return (0);
1585 		}
1586 	}
1587 
1588 
1589 	/* check that we only have digits and '-' */
1590 	i = strspn(from + 1, "0123456789-") + 1;
1591 	if (i < strlen(from)) {
1592 		print_error(pos,
1593 		    gettext("Invalid %s \"%s\": invalid character '%c'.\n"),
1594 		    ID_SID, from, from[i]);
1595 		return (0);
1596 	}
1597 
1598 
1599 	cp = from + strlen("S-1-");
1600 
1601 	/* 64-bit safe parsing of unsigned 48-bit authority value */
1602 	errno = 0;
1603 	a = strtoull(cp, &ecp, 10);
1604 
1605 	/* errors parsing the authority or too many bits */
1606 	if (cp == ecp || (a == 0 && errno == EINVAL)) {
1607 		print_error(pos,
1608 		    gettext("Invalid %s \"%s\": unable to parse the "
1609 		    "authority \"%.*s\".\n"), ID_SID, from, ecp - cp,
1610 		    cp);
1611 		return (0);
1612 	}
1613 
1614 	if ((a == ULLONG_MAX && errno == ERANGE) ||
1615 	    (a & 0x0000ffffffffffffULL) != a) {
1616 		print_error(pos,
1617 		    gettext("Invalid %s \"%s\": the authority "
1618 		    "\"%.*s\" is too large.\n"), ID_SID, from,
1619 		    ecp - cp, cp);
1620 		return (0);
1621 	}
1622 
1623 	cp = ecp;
1624 
1625 	if (j < 3) {
1626 		print_error(pos,
1627 		    gettext("Invalid %s \"%s\": must have at least one RID.\n"),
1628 		    ID_SID, from);
1629 		return (0);
1630 	}
1631 
1632 	for (i = 2; i < j; i++) {
1633 		if (*cp++ != '-') {
1634 			/* Should never happen */
1635 			print_error(pos,
1636 			    gettext("Invalid %s \"%s\": internal error:"
1637 			    " '-' missing.\n"),
1638 			    ID_SID, from);
1639 			return (0);
1640 		}
1641 		/* 32-bit safe parsing of unsigned 32-bit RID */
1642 		errno = 0;
1643 		r = strtoul(cp, &ecp, 10);
1644 
1645 		/* errors parsing the RID */
1646 		if (cp == ecp || (r == 0 && errno == EINVAL)) {
1647 			/* should never happen */
1648 			print_error(pos,
1649 			    gettext("Invalid %s \"%s\": internal error: "
1650 			    "unable to parse the RID "
1651 			    "after \"%.*s\".\n"), ID_SID,
1652 			    from, cp - from, from);
1653 			return (0);
1654 		}
1655 
1656 		if (r == ULONG_MAX && errno == ERANGE) {
1657 			print_error(pos,
1658 			    gettext("Invalid %s \"%s\": the RID \"%.*s\""
1659 			    " is too large.\n"), ID_SID,
1660 			    from, ecp - cp, cp);
1661 			return (0);
1662 		}
1663 		prefix_end = cp;
1664 		cp = ecp;
1665 	}
1666 
1667 	/* check that all of the string SID has been consumed */
1668 	if (*cp != '\0') {
1669 		/* Should never happen */
1670 		print_error(pos,
1671 		    gettext("Invalid %s \"%s\": internal error: "
1672 		    "something is still left.\n"),
1673 		    ID_SID, from);
1674 		return (0);
1675 	}
1676 
1677 	*rid = (idmap_rid_t)r;
1678 
1679 	/* -1 for the '-' at the end: */
1680 	*prefix = strndup(from, prefix_end - from - 1);
1681 	if (*prefix == NULL) {
1682 		print_error(pos,
1683 		    gettext("Not enough memory.\n"));
1684 		return (0);
1685 	}
1686 
1687 	return (1);
1688 }
1689 
1690 /* Does the line start with USERMAP_CFG IP qualifier? */
1691 static int
1692 ucp_is_IP_qualifier(char *line)
1693 {
1694 	char *it;
1695 	it = line + strcspn(line, " \t\n#:");
1696 	return (*(it + 1) == ':' ? 1 : 0);
1697 }
1698 
1699 
1700 /*
1701  * returns interior of quotation marks in USERMAP_CFG. In this format,
1702  * there cannot be a protected quotation mark inside.
1703  */
1704 static char *
1705 ucp_qm_interior(char **line, cmd_pos_t *pos)
1706 {
1707 	char *out;
1708 	char *qm = strchr(*line + 1, '"');
1709 	if (qm == NULL) {
1710 		print_error(pos,
1711 		    gettext("Unclosed quotations\n"));
1712 		return (NULL);
1713 	}
1714 
1715 	out = strndup(*line + 1, qm - *line - 1);
1716 	*line = qm + 1;
1717 	return (out);
1718 }
1719 
1720 /*
1721  * Grab next token from the line in USERMAP_CFG format. terminators,
1722  * the 3rd parameter, contains all the characters which can terminate
1723  * the token. line_num is the line number of input used for error
1724  * reporting.
1725  */
1726 static char *
1727 ucp_grab_token(char **line, cmd_pos_t *pos, const char *terminators)
1728 {
1729 	char *token;
1730 	if (**line == '"')
1731 		token = ucp_qm_interior(line, pos);
1732 	else {
1733 		int length = strcspn(*line, terminators);
1734 		token = strndup(*line, length);
1735 		*line += length;
1736 	}
1737 
1738 	return (token);
1739 }
1740 
1741 
1742 /*
1743  * Convert a line in usermap.cfg format to name_mapping.
1744  *
1745  * Return values: -1 for error, 0 for empty line, 1 for a mapping
1746  * found.
1747  */
1748 static int
1749 ucp_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm)
1750 {
1751 	char *it;
1752 	char *token;
1753 	char *token2;
1754 	char separator;
1755 	int is_direction = 0;
1756 
1757 	it = line + strspn(line, " \t\n");
1758 
1759 	/* empty or comment lines are OK: */
1760 	if (*it == '\0' || *it == '#')
1761 		return (0);
1762 
1763 	/* We do not support network qualifiers */
1764 	if (ucp_is_IP_qualifier(it)) {
1765 		print_error(pos,
1766 		    gettext("Unable to handle network qualifier.\n"));
1767 		return (-1);
1768 	}
1769 
1770 	/* The windows name: */
1771 	token = ucp_grab_token(&it, pos, " \t#\\\n@=<");
1772 	if (token == NULL)
1773 		return (-1);
1774 
1775 	separator = *it;
1776 
1777 	/* Didn't we bump to the end of line? */
1778 	if (separator == '\0' || separator == '#') {
1779 		free(token);
1780 		print_error(pos,
1781 		    gettext("UNIX_name not found.\n"));
1782 		return (-1);
1783 	}
1784 
1785 	/* Do we have a domainname? */
1786 	if (separator == '\\' || separator == '@') {
1787 		it ++;
1788 		token2 = ucp_grab_token(&it, pos, " \t\n#");
1789 		if (token2 == NULL) {
1790 			free(token);
1791 			return (-1);
1792 		} else if (*it == '\0' || *it == '#') {
1793 			free(token);
1794 			free(token2);
1795 			print_error(pos,
1796 			    gettext("UNIX_name not found.\n"));
1797 		}
1798 
1799 		if (separator == '\\') {
1800 			nm->windomain = token;
1801 			nm->winname = token2;
1802 			nm->is_nt4 = 1;
1803 		} else {
1804 			nm->windomain = token2;
1805 			nm->winname = token;
1806 			nm->is_nt4 = 0;
1807 
1808 		}
1809 	} else {
1810 		nm->windomain = NULL;
1811 		nm->winname = token;
1812 		nm->is_nt4 = 0;
1813 	}
1814 
1815 
1816 	it = it + strspn(it, " \t\n");
1817 
1818 	/* Direction string is optional: */
1819 	if (strncmp(it, "==", 2) == 0) {
1820 		nm->direction = IDMAP_DIRECTION_BI;
1821 		is_direction = 1;
1822 	} else if (strncmp(it, "<=", 2) == 0) {
1823 		nm->direction = IDMAP_DIRECTION_U2W;
1824 		is_direction = 1;
1825 	} else if (strncmp(it, "=>", 2) == 0) {
1826 		nm->direction = IDMAP_DIRECTION_W2U;
1827 		is_direction = 1;
1828 	} else {
1829 		nm->direction = IDMAP_DIRECTION_BI;
1830 		is_direction = 0;
1831 	}
1832 
1833 	if (is_direction) {
1834 		it += 2;
1835 		it += strspn(it, " \t\n");
1836 
1837 		if (*it == '\0' || *it == '#') {
1838 			print_error(pos,
1839 			    gettext("UNIX_name not found.\n"));
1840 			return (-1);
1841 		}
1842 	}
1843 
1844 	/* Now unixname: */
1845 	it += strspn(it, " \t\n");
1846 	token = ucp_grab_token(&it, pos, " \t\n#");
1847 
1848 	if (token == NULL)
1849 		/* nm->winname to be freed by name_mapping_fini */
1850 		return (-1);
1851 
1852 	/* Neither here we support IP qualifiers */
1853 	if (ucp_is_IP_qualifier(token)) {
1854 		print_error(pos,
1855 		    gettext("Unable to handle network qualifier.\n"));
1856 		free(token);
1857 		return (-1);
1858 	}
1859 
1860 	nm->unixname = token;
1861 
1862 	it += strspn(it, " \t\n");
1863 
1864 	/* Does something remain on the line */
1865 	if (*it  != '\0' && *it != '#') {
1866 		print_error(pos,
1867 		    gettext("Unrecognized parameters \"%s\".\n"), it);
1868 		return (-1);
1869 	}
1870 
1871 	return (1);
1872 }
1873 
1874 /*
1875  * Parse SMBUSERS line to name_mapping_t. if line is NULL, then
1876  * pasrsing of the previous line is continued. line_num is input line
1877  * number used for error reporting.
1878  * Return values:
1879  *    rc -1: error
1880  *    rc = 0: mapping found and the line is finished,
1881  *    rc = 1: mapping found and there remains other on the line
1882  */
1883 static int
1884 sup_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm)
1885 {
1886 	static char *ll = NULL;
1887 	static char *unixname = NULL;
1888 	static size_t unixname_l = 0;
1889 	char *token;
1890 
1891 	if (line != NULL) {
1892 		ll = line;
1893 
1894 		unixname = ll += strspn(ll, " \t");
1895 		if (*ll == '\0' || *ll == '#')
1896 			return (0);
1897 
1898 		unixname_l = strcspn(ll, " \t:=#\n");
1899 		ll += unixname_l;
1900 
1901 		if (*ll == '\0'|| *ll == '#')
1902 			return (0);
1903 
1904 		ll +=  strspn(ll, " \t:=#\n");
1905 
1906 	}
1907 
1908 	if (*ll == '\0'|| *ll == '#')
1909 		return (0);
1910 
1911 	token = ucp_grab_token(&ll, pos, " \t\n");
1912 	if (token == NULL)
1913 		return (-1);
1914 
1915 	nm->is_nt4 = 0;
1916 	nm->direction = IDMAP_DIRECTION_W2U;
1917 
1918 	nm->windomain = NULL;
1919 	nm->winname = token;
1920 	nm->unixname = strndup(unixname, unixname_l);
1921 	if (nm->unixname == NULL)
1922 		return (-1);
1923 
1924 	ll += strspn(ll, " \t\n");
1925 	return (1);
1926 }
1927 
1928 /* Parse line to name_mapping_t. Basicaly just a format switch. */
1929 static int
1930 line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm, format_t f)
1931 {
1932 	switch (f) {
1933 	case USERMAP_CFG:
1934 		if (line == NULL)
1935 			return (0);
1936 		else
1937 			return (ucp_line2nm(line, pos, nm));
1938 	case SMBUSERS:
1939 		return (sup_line2nm(line, pos, nm));
1940 	default:
1941 		/* This can never happen */
1942 		print_error(pos,
1943 		    gettext("Internal error: invalid line format.\n"));
1944 	}
1945 
1946 	return (-1);
1947 }
1948 
1949 
1950 /* Examine -f flag and return the appropriate format_t */
1951 static format_t
1952 ff2format(char *ff, int is_mandatory)
1953 {
1954 
1955 	if (ff == NULL && is_mandatory) {
1956 		print_error(NULL, gettext("Format not given.\n"));
1957 		return (UNDEFINED_FORMAT);
1958 	}
1959 
1960 	if (ff == NULL)
1961 		return (DEFAULT_FORMAT);
1962 
1963 	if (strcasecmp(ff, "usermap.cfg") == 0)
1964 		return (USERMAP_CFG);
1965 
1966 	if (strcasecmp(ff, "smbusers") == 0)
1967 		return (SMBUSERS);
1968 
1969 	print_error(NULL,
1970 	    gettext("The only known formats are: \"usermap.cfg\" and "
1971 	    "\"smbusers\".\n"));
1972 	return (UNDEFINED_FORMAT);
1973 }
1974 
1975 /* Delete all namerules of the given type */
1976 static int
1977 flush_nm(boolean_t is_user, cmd_pos_t *pos)
1978 {
1979 	idmap_stat stat;
1980 
1981 	stat = idmap_udt_flush_namerules(udt);
1982 	if (stat < 0) {
1983 		print_error(pos,
1984 		    is_user ? gettext("Unable to flush users (%s).\n")
1985 		    : gettext("Unable to flush groups (%s).\n"),
1986 		    idmap_stat2string(handle, stat));
1987 		return (-1);
1988 	}
1989 
1990 	if (positions_add(pos) < 0)
1991 		return (-1);
1992 
1993 	return (0);
1994 }
1995 
1996 /* import command handler */
1997 static int
1998 /* LINTED E_FUNC_ARG_UNUSED */
1999 do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2000 {
2001 	name_mapping_t *nm;
2002 	cmd_pos_t pos2;
2003 	char line[MAX_INPUT_LINE_SZ];
2004 	format_t format;
2005 	int rc = 0;
2006 	idmap_stat stat;
2007 	FILE *file = NULL;
2008 
2009 	if (batch_mode) {
2010 		print_error(pos,
2011 		    gettext("Import is not allowed in the batch mode.\n"));
2012 		return (-1);
2013 	}
2014 
2015 	format = ff2format(argv[0], 1);
2016 	if (format == UNDEFINED_FORMAT)
2017 		return (-1);
2018 
2019 	if (init_udt_command())
2020 		return (-1);
2021 
2022 	/* We don't flush groups in the usermap.cfg nor smbusers format */
2023 	if (f[F_FLAG] != NULL &&
2024 	    flush_nm(B_TRUE, pos) < 0 &&
2025 	    (format == USERMAP_CFG || format == SMBUSERS ||
2026 	    flush_nm(B_FALSE, pos) < 0)) {
2027 		rc = -1;
2028 		goto cleanup;
2029 	}
2030 
2031 	/* Where we import from? */
2032 	if (f[f_FLAG] == NULL)
2033 		file = stdin;
2034 	else {
2035 		file = fopen(f[f_FLAG], "r");
2036 		if (file == NULL) {
2037 			perror(f[f_FLAG]);
2038 			goto cleanup;
2039 		}
2040 	}
2041 
2042 	pos2.linenum = 0;
2043 	pos2.line = line;
2044 
2045 	while (fgets(line, MAX_INPUT_LINE_SZ, file)) {
2046 		char *line2 = line;
2047 		pos2.linenum++;
2048 
2049 		/*
2050 		 * In SMBUSERS format there can be more mappings on
2051 		 * each line. So we need the internal cycle for each line.
2052 		 */
2053 		do {
2054 			nm = name_mapping_init();
2055 			if (nm == NULL) {
2056 				rc = -1;
2057 				goto cleanup;
2058 			}
2059 
2060 			rc = line2nm(line2, &pos2, nm, format);
2061 			line2 = NULL;
2062 
2063 			if (rc < 1) {
2064 				name_mapping_fini(nm);
2065 				break;
2066 			}
2067 
2068 			stat = idmap_udt_add_namerule(udt, nm->windomain,
2069 			    nm->is_user ? B_TRUE : B_FALSE,
2070 			    nm->is_wuser ? B_TRUE : B_FALSE,
2071 			    nm->winname,
2072 			    nm->unixname, nm->is_nt4, nm->direction);
2073 			if (stat < 0) {
2074 				print_error(&pos2,
2075 				    gettext("Transaction error (%s)\n"),
2076 				    idmap_stat2string(handle, stat));
2077 				rc = -1;
2078 			}
2079 
2080 			if (rc >= 0)
2081 				rc = positions_add(&pos2);
2082 
2083 			name_mapping_fini(nm);
2084 
2085 		} while (rc >= 0);
2086 
2087 		if (rc < 0) {
2088 			print_error(NULL,
2089 			    gettext("Import canceled.\n"));
2090 			break;
2091 		}
2092 	}
2093 
2094 cleanup:
2095 	if (fini_udt_command((rc < 0 ? 0 : 1), pos))
2096 		rc = -1;
2097 	if (file != NULL && file != stdin)
2098 		(void) fclose(file);
2099 	return (rc);
2100 }
2101 
2102 
2103 /*
2104  * List name mappings in the format specified. list_users /
2105  * list_groups determine which type to list. The output goes to the
2106  * file fi.
2107  */
2108 static int
2109 list_name_mappings(format_t format, FILE *fi)
2110 {
2111 	idmap_stat stat;
2112 	idmap_iter_t *ihandle;
2113 	name_mapping_t *nm;
2114 	boolean_t is_user;
2115 	boolean_t is_wuser;
2116 	print_handle_t *ph;
2117 
2118 	stat = idmap_iter_namerules(handle, NULL, 0, 0, NULL, NULL, &ihandle);
2119 	if (stat < 0) {
2120 		print_error(NULL,
2121 		    gettext("Iteration handle not obtained (%s)\n"),
2122 		    idmap_stat2string(handle, stat));
2123 		idmap_iter_destroy(ihandle);
2124 		return (-1);
2125 	}
2126 
2127 	ph = print_mapping_init(format, fi);
2128 	if (ph == NULL)
2129 		return (-1);
2130 
2131 	do {
2132 		nm = name_mapping_init();
2133 		if (nm == NULL) {
2134 			idmap_iter_destroy(ihandle);
2135 			return (-1);
2136 		}
2137 
2138 		stat = idmap_iter_next_namerule(ihandle, &nm->windomain,
2139 		    &nm->winname, &nm->unixname, &is_user, &is_wuser,
2140 		    &nm->is_nt4, &nm->direction);
2141 		if (stat >= 0) {
2142 			nm->is_user = is_user ? I_YES : I_NO;
2143 			nm->is_wuser = is_wuser ? I_YES : I_NO;
2144 			(void) print_mapping(ph, nm);
2145 		}
2146 
2147 		name_mapping_fini(nm);
2148 
2149 	} while (stat > 0);
2150 
2151 	(void) print_mapping_fini(ph);
2152 
2153 	if (stat < 0 && stat !=  IDMAP_ERR_NOTFOUND) {
2154 		print_error(NULL,
2155 		    gettext("Error during iteration (%s)\n"),
2156 		    idmap_stat2string(handle, stat));
2157 		idmap_iter_destroy(ihandle);
2158 		return (-1);
2159 	}
2160 
2161 	idmap_iter_destroy(ihandle);
2162 	return (0);
2163 }
2164 
2165 /* Export command handler  */
2166 static int
2167 /* LINTED E_FUNC_ARG_UNUSED */
2168 do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2169 {
2170 	int rc;
2171 	format_t format;
2172 	FILE *fi;
2173 
2174 	format = ff2format(argv[0], 1);
2175 	if (format == UNDEFINED_FORMAT)
2176 		return (-1);
2177 
2178 	/* Where do we output to? */
2179 	if (f[f_FLAG] == NULL)
2180 		fi = stdout;
2181 	else {
2182 		fi = fopen(f[f_FLAG], "w");
2183 		if (fi == NULL) {
2184 			perror(f[f_FLAG]);
2185 			return (-1);
2186 		}
2187 	}
2188 
2189 	if (init_command() < 0) {
2190 		rc = -1;
2191 		goto cleanup;
2192 	}
2193 
2194 	/* List the requested types: */
2195 	rc = list_name_mappings(format, fi);
2196 
2197 	fini_command();
2198 
2199 cleanup:
2200 	if (fi != NULL && fi != stdout)
2201 		(void) fclose(fi);
2202 	return (rc);
2203 }
2204 
2205 /* List command handler */
2206 static int
2207 /* LINTED E_FUNC_ARG_UNUSED */
2208 do_list_name_mappings(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2209 {
2210 	int rc;
2211 
2212 	if (init_command()) {
2213 		return (-1);
2214 	}
2215 
2216 	/* List the requested types: */
2217 	rc = list_name_mappings(DEFAULT_FORMAT, stdout);
2218 
2219 	fini_command();
2220 	return (rc);
2221 }
2222 
2223 /* This is just a debug function for dumping flags */
2224 static void
2225 print_flags(flag_t *f)
2226 {
2227 	int c;
2228 	for (c = 0; c < FLAG_ALPHABET_SIZE; c++) {
2229 		if (f[c] == FLAG_SET)
2230 			(void) printf("FLAG: -%c, VALUE: %p\n", c,
2231 			    (void *) f[c]);
2232 		else if (f[c])
2233 			(void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]);
2234 	}
2235 }
2236 
2237 
2238 /*
2239  * Split argument to its identity code and a name part
2240  * return values:
2241  *    -1 for unknown identity
2242  *     0 for no identity
2243  *     <TYPE_XXX> for known identity
2244  */
2245 
2246 static int
2247 get_identity(char *arg, char **name, cmd_pos_t *pos)
2248 {
2249 	int i;
2250 	char *it;
2251 	int code;
2252 
2253 	if ((it = strchr(arg, ':')) == NULL) {
2254 		*name = arg;
2255 		return (0);
2256 	}
2257 
2258 
2259 	*it = '\0';
2260 	for (i = 0, code = 0;
2261 	    i < sizeof (identity2code) / sizeof (id_code_t);
2262 	    i++) {
2263 		if (strcmp(identity2code[i].identity, arg) == 0) {
2264 			code = identity2code[i].code;
2265 			break;
2266 		}
2267 	}
2268 
2269 	/* restore the original string: */
2270 	*it = ':';
2271 
2272 	if (!code) {
2273 		print_error(pos,
2274 		    gettext("Error: invalid identity type \"%.*s\"\n"),
2275 		    it - arg, arg);
2276 		return (-1);
2277 	}
2278 
2279 	*name = it + 1;
2280 	return (code);
2281 }
2282 
2283 
2284 /*
2285  * This function splits name to the relevant pieces: is_user, winname,
2286  * windomain unixname. E.g. for winname, it strdups nm->winname and possibly
2287  * nm->windomain and return TYPE_WN.
2288  *
2289  * If there is already one of the text fields allocated, it is OK.
2290  * Return values:
2291  *     -1 ... syntax error
2292  *     0 ... it wasnt possible to determine
2293  *     <TYPE_XXX> otherwise
2294  */
2295 
2296 static int
2297 name2parts(char *name, name_mapping_t *nm, cmd_pos_t *pos)
2298 {
2299 	char *it;
2300 	int code;
2301 
2302 	code = get_identity(name, &it, pos);
2303 
2304 	switch (code) {
2305 	case -1:
2306 		/* syntax error: */
2307 		return (-1);
2308 	case 0:
2309 		/* autodetection: */
2310 		if (nm->winname != NULL && nm->is_wuser != I_UNKNOWN)
2311 			code = nm->is_wuser == I_YES ? TYPE_UU : TYPE_UG;
2312 		else if (nm->unixname != NULL ||
2313 		    strchr(name, '@') != NULL ||
2314 		    strchr(name, '\\') != NULL)
2315 			/* btw, nm->is_user can never be I_UNKNOWN here */
2316 			code = TYPE_WN;
2317 		else
2318 			return (0);
2319 		/* If the code was guessed succesfully, we are OK. */
2320 		break;
2321 	default:
2322 		name = it;
2323 	}
2324 
2325 	if (code & IS_WIN) {
2326 		if (code & IS_USER)
2327 			nm->is_wuser = I_YES;
2328 		else if (code & IS_GROUP)
2329 			nm->is_wuser = I_NO;
2330 	} else {
2331 		if (code & IS_USER)
2332 			nm->is_user = I_YES;
2333 		else if (code & IS_GROUP)
2334 			nm->is_user = I_NO;
2335 	}
2336 
2337 	if (code & IS_WIN && code & IS_NAME) {
2338 		if (nm->winname != NULL || nm->windomain != NULL)
2339 			return (code);
2340 
2341 		if ((it = strchr(name, '@')) != NULL) {
2342 			int length = it - name + 1;
2343 			nm->winname = (char *)malloc(length);
2344 			(void) strncpy(nm->winname, name, length - 1);
2345 			nm->winname[length - 1] = '\0';
2346 			nm->windomain = strdup(it + 1);
2347 		} else if ((it = strrchr(name, '\\')) != NULL) {
2348 			int length = it - name + 1;
2349 			nm->windomain = (char *)malloc(length);
2350 			(void) strncpy(nm->windomain, name, length - 1);
2351 			nm->windomain[length - 1] = '\0';
2352 			nm->winname = strdup(it + 1);
2353 			nm->is_nt4 = B_TRUE;
2354 		} else
2355 			nm->winname = strdup(name);
2356 
2357 		return (code);
2358 	}
2359 
2360 
2361 	if (!(code & IS_WIN) && code & IS_NAME) {
2362 		if (nm->unixname != NULL)
2363 			return (code);
2364 
2365 		if (strlen(name) == 0)
2366 			nm->unixname = strdup("\"\"");
2367 		else
2368 			nm->unixname = strdup(name);
2369 		return (code);
2370 	}
2371 
2372 
2373 	if (code & IS_WIN && !(code & IS_NAME)) {
2374 		if (!sid_convert(name, &nm->sidprefix, &nm->rid, pos))
2375 			return (-1);
2376 		else
2377 			return (code);
2378 	}
2379 
2380 /*
2381  * it is (!(code & TYPE_WIN) &&  !(code & TYPE_NAME)) here - the other
2382  * possiblities are exhausted.
2383  */
2384 
2385 	if (!pid_convert(name, &nm->pid, code, pos))
2386 			return (-1);
2387 		else
2388 			return (code);
2389 
2390 }
2391 
2392 /*
2393  * Cycle through add/remove arguments until they are identified or found
2394  * invalid.
2395  */
2396 static
2397 int
2398 args2nm(name_mapping_t *nm, int *is_first_win, int argc, char **argv,
2399     cmd_pos_t *pos)
2400 {
2401 	int code;
2402 	int i;
2403 
2404 	for (i = 0; i < 2 * argc - 1; i++) {
2405 		code = name2parts(argv[i % 2], nm, pos);
2406 		switch (code) {
2407 			case -1:
2408 				return (-1);
2409 		case 0:
2410 			if (i > 0) {
2411 				print_error(pos,
2412 				    gettext("Missing identity type"
2413 				    " cannot be determined for %s.\n"),
2414 				    argv[i % 2]);
2415 				return (-1);
2416 			}
2417 			break;
2418 		default:
2419 			if (!(code & IS_NAME)) {
2420 				print_error(pos,
2421 				    gettext("%s is not a valid name\n"),
2422 				    argv[i % 2]);
2423 				return (-1);
2424 			}
2425 		}
2426 	}
2427 
2428 	if (argc == 2 && nm->winname == NULL) {
2429 		print_error(pos, gettext("No windows identity found.\n"));
2430 		return (-1);
2431 	}
2432 	if (argc == 2 && nm->unixname == NULL) {
2433 		print_error(pos, gettext("No unix identity found.\n"));
2434 		return (-1);
2435 	}
2436 
2437 	*is_first_win = code & IS_WIN;
2438 	return (0);
2439 
2440 
2441 }
2442 
2443 
2444 
2445 /* add command handler. */
2446 static int
2447 do_add_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2448 {
2449 	name_mapping_t *nm;
2450 	int rc = 0;
2451 	int is_first_win;
2452 	idmap_stat stat;
2453 	int is_wuser;
2454 	print_handle_t *ph;
2455 
2456 
2457 
2458 	/* Exactly two arguments must be specified */
2459 	if (argc < 2) {
2460 		print_error(pos, gettext("Not enough arguments.\n"));
2461 		return (-1);
2462 	} else if (argc > 2)  {
2463 		print_error(pos, gettext("Too many arguments.\n"));
2464 		return (-1);
2465 	}
2466 
2467 	nm = name_mapping_init();
2468 	if (nm == NULL)
2469 		return (-1);
2470 
2471 	if (args2nm(nm, &is_first_win, argc, argv, pos) < 0) {
2472 		name_mapping_fini(nm);
2473 		return (-1);
2474 	}
2475 
2476 	if (f[d_FLAG] != NULL)
2477 		nm->direction = is_first_win
2478 		    ? IDMAP_DIRECTION_W2U
2479 		    : IDMAP_DIRECTION_U2W;
2480 	else
2481 		nm->direction = IDMAP_DIRECTION_BI;
2482 
2483 	/* Now let us write it: */
2484 
2485 	if (init_udt_command()) {
2486 		name_mapping_fini(nm);
2487 		return (-1);
2488 	}
2489 
2490 	for (is_wuser = I_YES; is_wuser >= I_NO; is_wuser--) {
2491 		/* nm->is_wuser can be I_YES, I_NO or I_UNKNOWN */
2492 		if ((is_wuser == I_YES && nm->is_wuser == I_NO) ||
2493 		    (is_wuser == I_NO && nm->is_wuser == I_YES))
2494 			continue;
2495 
2496 		stat = idmap_udt_add_namerule(udt, nm->windomain,
2497 		    nm->is_user ? B_TRUE : B_FALSE,
2498 		    is_wuser ? B_TRUE : B_FALSE,
2499 		    nm->winname, nm->unixname, nm->is_nt4, nm->direction);
2500 	}
2501 
2502 	/* We echo the mapping */
2503 	ph = print_mapping_init(DEFAULT_FORMAT, stdout);
2504 	if (ph == NULL) {
2505 		rc = -1;
2506 		goto cleanup;
2507 	}
2508 	(void) print_mapping(ph, nm);
2509 	(void) print_mapping_fini(ph);
2510 
2511 	if (stat < 0) {
2512 		print_error(pos,
2513 		    gettext("Mapping not created (%s)\n"),
2514 		    idmap_stat2string(handle, stat));
2515 		rc = -1;
2516 	}
2517 
2518 	if (rc == 0)
2519 		rc = positions_add(pos);
2520 
2521 cleanup:
2522 	name_mapping_fini(nm);
2523 	if (fini_udt_command(1, pos))
2524 		rc = -1;
2525 	return (rc);
2526 }
2527 
2528 /* remove command handler */
2529 static int
2530 do_remove_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2531 {
2532 	name_mapping_t *nm;
2533 	int rc = 0;
2534 	idmap_stat stat;
2535 	int is_first_win;
2536 	int is_wuser;
2537 
2538 	/* "-a" means we flush all of them */
2539 	if (f[a_FLAG] != NULL) {
2540 		if (argc) {
2541 			print_error(pos,
2542 			    gettext("Too many arguments.\n"));
2543 			return (-1);
2544 		}
2545 
2546 		if (init_udt_command())
2547 			return (-1);
2548 		rc = flush_nm(B_TRUE, pos);
2549 
2550 		if (rc >= 0)
2551 			rc = flush_nm(B_FALSE, pos);
2552 
2553 		if (fini_udt_command(rc ? 0 : 1, pos))
2554 			rc = -1;
2555 		return (rc);
2556 	}
2557 
2558 	/* Contrary to add_name_mapping, we can have only one argument */
2559 	if (argc < 1) {
2560 		print_error(pos, gettext("Not enough arguments.\n"));
2561 		return (-1);
2562 	} else if (argc > 2) {
2563 		print_error(pos, gettext("Too many arguments.\n"));
2564 		return (-1);
2565 	} else if (
2566 		/* both -f and -t: */
2567 	    f[f_FLAG] != NULL && f[t_FLAG] != NULL ||
2568 		/* -d with a single argument: */
2569 	    argc == 1 && f[d_FLAG] != NULL ||
2570 		/* -f or -t with two arguments: */
2571 	    argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) {
2572 		print_error(pos,
2573 		    gettext("Direction ambiguous.\n"));
2574 		return (-1);
2575 	}
2576 
2577 
2578 	/*
2579 	 * Similar to do_add_name_mapping - see the comments
2580 	 * there. Except we may have only one argument here.
2581 	 */
2582 	nm = name_mapping_init();
2583 	if (nm == NULL)
2584 		return (-1);
2585 
2586 	if (args2nm(nm, &is_first_win, argc, argv, pos) < 0) {
2587 		name_mapping_fini(nm);
2588 		return (-1);
2589 	}
2590 
2591 	/*
2592 	 * If the direction is not specified by a -d/-f/-t flag, then it
2593 	 * is IDMAP_DIRECTION_UNDEF, because in that case we want to
2594 	 * remove any mapping. If it was IDMAP_DIRECTION_BI, idmap_api would
2595 	 * delete a bidirectional one only.
2596 	 */
2597 	if (f[d_FLAG] != NULL || f[f_FLAG] != NULL)
2598 		nm->direction = is_first_win
2599 		    ? IDMAP_DIRECTION_W2U
2600 		    : IDMAP_DIRECTION_U2W;
2601 	else if (f[t_FLAG] != NULL)
2602 		nm->direction = is_first_win
2603 		    ? IDMAP_DIRECTION_U2W
2604 		    : IDMAP_DIRECTION_W2U;
2605 	else
2606 		nm->direction = IDMAP_DIRECTION_UNDEF;
2607 
2608 	if (init_udt_command()) {
2609 		name_mapping_fini(nm);
2610 		return (-1);
2611 	}
2612 
2613 	for (is_wuser = I_YES; is_wuser >= I_NO; is_wuser--) {
2614 		if ((is_wuser == I_YES && nm->is_wuser == I_NO) ||
2615 		    (is_wuser == I_NO && nm->is_wuser == I_YES))
2616 			continue;
2617 
2618 		stat = idmap_udt_rm_namerule(udt,
2619 		    nm->is_user ? B_TRUE : B_FALSE,
2620 		    is_wuser ? B_TRUE : B_FALSE,
2621 		    nm->windomain, nm->winname, nm->unixname, nm->direction);
2622 
2623 		if (stat < 0) {
2624 			print_error(pos,
2625 			    gettext("Mapping not deleted (%s)\n"),
2626 			    idmap_stat2string(handle, stat));
2627 			rc = -1;
2628 			break;
2629 		}
2630 	}
2631 
2632 	if (rc == 0)
2633 		rc = positions_add(pos);
2634 
2635 cleanup:
2636 	name_mapping_fini(nm);
2637 	if (fini_udt_command(1, pos))
2638 		rc = -1;
2639 	return (rc);
2640 }
2641 
2642 
2643 /* exit command handler */
2644 static int
2645 /* LINTED E_FUNC_ARG_UNUSED */
2646 do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2647 {
2648 	return (0);
2649 }
2650 
2651 
2652 /* debug command handler: just print the parameters */
2653 static int
2654 /* LINTED E_STATIC_UNUSED */
2655 debug_print_params(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2656 {
2657 	int i;
2658 #if 0
2659 	char *leaktest = (char *)malloc(100);
2660 #endif
2661 
2662 	print_flags(f);
2663 
2664 	for (i = 0; i < argc; i++) {
2665 		(void) printf("Argument %d: %s\n", i, argv[i]);
2666 	}
2667 
2668 	(void) fflush(stdout);
2669 	return (0);
2670 }
2671 
2672 /*
2673  * From name_mapping_t, asseble a string containing identity of the
2674  * given type.
2675  */
2676 static int
2677 nm2type(name_mapping_t *nm, int type, char **to)
2678 {
2679 	switch (type) {
2680 	case TYPE_SID:
2681 	case TYPE_USID:
2682 	case TYPE_GSID:
2683 		if (nm->sidprefix == NULL)
2684 			return (-1);
2685 		*to = sid_format(nm);
2686 		return (0);
2687 	case TYPE_WN:
2688 	case TYPE_WU:
2689 	case TYPE_WG:
2690 		return (nm2winqn(nm, to));
2691 	case TYPE_UID:
2692 	case TYPE_GID:
2693 	case TYPE_PID:
2694 		*to = pid_format(nm->pid, nm->is_user);
2695 		if (*to == NULL)
2696 			return (-1);
2697 		else
2698 			return (0);
2699 	case TYPE_UN:
2700 	case TYPE_UU:
2701 	case TYPE_UG:
2702 		return (nm2unixname(nm, to));
2703 	default:
2704 		/* This can never happen: */
2705 		print_error(NULL,
2706 		    gettext("Internal error: invalid name type.\n"));
2707 		return (-1);
2708 	}
2709 	/* never reached */
2710 }
2711 
2712 /* show command handler */
2713 static int
2714 do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2715 {
2716 	idmap_stat stat = 0;
2717 	int flag;
2718 	idmap_stat map_stat = 0;
2719 	int type_from;
2720 	int type_to;
2721 	name_mapping_t *nm = NULL;
2722 	char *fromname;
2723 	char *toname;
2724 	idmap_info info;
2725 
2726 	(void) memset(&info, 0, sizeof (info));
2727 
2728 	if (argc == 0) {
2729 		print_error(pos,
2730 		    gettext("No identity given\n"));
2731 		return (-1);
2732 	} else if (argc > 2) {
2733 		print_error(pos,
2734 		    gettext("Too many arguments.\n"));
2735 		return (-1);
2736 	}
2737 
2738 	flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC;
2739 	flag |= f[v_FLAG] == NULL ? 0 : IDMAP_REQ_FLG_MAPPING_INFO;
2740 
2741 	if (init_command())
2742 		return (-1);
2743 
2744 	nm = name_mapping_init();
2745 	if (nm == NULL)
2746 		goto cleanup;
2747 
2748 	type_from = name2parts(argv[0], nm, pos);
2749 	if (type_from <= 0) {
2750 		stat = IDMAP_ERR_ARG;
2751 		goto cleanup;
2752 	}
2753 
2754 
2755 	/* Second, determine type_to: */
2756 	if (argc < 2) {
2757 		type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID;
2758 		if (type_from & IS_NAME)
2759 			type_to |= IS_NAME;
2760 	} else {
2761 		int i;
2762 
2763 		for (i = 0, type_to = 0;
2764 		    i < sizeof (identity2code) / sizeof (id_code_t);
2765 		    i++) {
2766 			if (strcmp(identity2code[i].identity, argv[1]) == 0) {
2767 				type_to = identity2code[i].code;
2768 				break;
2769 			}
2770 		}
2771 
2772 		if (!type_to) {
2773 			print_error(pos,
2774 			    gettext("Error: invalid target type \"%s\"\n"),
2775 			    argv[1]);
2776 			stat = IDMAP_ERR_ARG;
2777 			goto cleanup;
2778 		}
2779 	}
2780 
2781 	if (type_to & IS_WIN) {
2782 		if (type_to & IS_USER)
2783 			nm->is_wuser = I_YES;
2784 		else if (type_to & IS_GROUP)
2785 			nm->is_wuser = I_NO;
2786 		else
2787 			nm->is_wuser = I_UNKNOWN;
2788 	} else {
2789 		if (type_to & IS_USER)
2790 			nm->is_user = I_YES;
2791 		else if (type_to & IS_GROUP)
2792 			nm->is_user = I_NO;
2793 	}
2794 
2795 	/* Are both arguments the same OS side? */
2796 	if (!(type_from & IS_WIN ^ type_to & IS_WIN)) {
2797 		print_error(pos,
2798 		    gettext("Direction ambiguous.\n"));
2799 		stat = IDMAP_ERR_ARG;
2800 		goto cleanup;
2801 	}
2802 
2803 /*
2804  * We have two interfaces for retrieving the mappings:
2805  * idmap_get_sidbyuid & comp (the batch interface) and
2806  * idmap_get_w2u_mapping & comp. We  want to use both of them, because
2807  * the former mimicks kernel interface better and the later offers the
2808  * string names. In the batch case, our batch has always size 1.
2809  *
2810  * Btw, type_from cannot be IDMAP_PID, because there is no type string
2811  * for it.
2812  */
2813 	if (type_from & IS_NAME || type_to & IS_NAME ||
2814 	    type_from  == TYPE_GSID || type_from  == TYPE_USID ||
2815 	    type_to  == TYPE_GSID || type_to  == TYPE_USID) {
2816 		if (type_from & IS_WIN) {
2817 			map_stat = idmap_get_w2u_mapping(handle,
2818 			    nm->sidprefix,
2819 			    &nm->rid,
2820 			    nm->winname,
2821 			    nm->windomain,
2822 			    flag,
2823 			    &nm->is_user, &nm->is_wuser,
2824 			    &nm->pid,
2825 			    &nm->unixname,
2826 			    &nm->direction,
2827 			    &info);
2828 		} else {
2829 			map_stat = idmap_get_u2w_mapping(handle,
2830 			    &nm->pid,
2831 			    nm->unixname,
2832 			    flag,
2833 			    nm->is_user, &nm->is_wuser,
2834 			    &nm->sidprefix,
2835 			    &nm->rid,
2836 			    &nm->winname,
2837 			    &nm->windomain,
2838 			    &nm->direction,
2839 			    &info);
2840 		}
2841 
2842 	} else {
2843 		/* batch handle */
2844 		idmap_get_handle_t *ghandle = NULL;
2845 		/* To be passed to idmap_get_uidbysid  */
2846 		gid_t gid = UNDEFINED_GID;
2847 		/* To be passed to idmap_get_gidbysid  */
2848 		uid_t uid = UNDEFINED_UID;
2849 
2850 
2851 		/* Create an in-memory structure for all the batch: */
2852 		stat = idmap_get_create(handle, &ghandle);
2853 		if (stat < 0) {
2854 			print_error(pos,
2855 			    gettext("Unable to create handle for communicating"
2856 			    " with idmapd(1M) (%s)\n"),
2857 			    idmap_stat2string(handle, stat));
2858 			idmap_get_destroy(ghandle);
2859 			goto cleanup;
2860 		}
2861 
2862 		/* Schedule the request: */
2863 		if (type_to == TYPE_UID) {
2864 			stat = idmap_getext_uidbysid(ghandle,
2865 			    nm->sidprefix,
2866 			    nm->rid,
2867 			    flag,
2868 			    &uid,
2869 			    &info,
2870 			    &map_stat);
2871 		} else if (type_to == TYPE_GID) {
2872 			stat =  idmap_getext_gidbysid(ghandle,
2873 			    nm->sidprefix,
2874 			    nm->rid,
2875 			    flag,
2876 			    &gid,
2877 			    &info,
2878 			    &map_stat);
2879 		} else if (type_to == TYPE_PID) {
2880 			stat = idmap_getext_pidbysid(ghandle,
2881 			    nm->sidprefix,
2882 			    nm->rid,
2883 			    flag,
2884 			    &nm->pid,
2885 			    &nm->is_user,
2886 			    &info,
2887 			    &map_stat);
2888 		} else if (type_from == TYPE_UID) {
2889 			stat = idmap_getext_sidbyuid(ghandle,
2890 			    nm->pid,
2891 			    flag,
2892 			    &nm->sidprefix,
2893 			    &nm->rid,
2894 			    &info,
2895 			    &map_stat);
2896 		} else if (type_from == TYPE_GID) {
2897 			stat = idmap_getext_sidbygid(ghandle,
2898 			    (gid_t)nm->pid,
2899 			    flag,
2900 			    &nm->sidprefix,
2901 			    &nm->rid,
2902 			    &info,
2903 			    &map_stat);
2904 		} else {
2905 			/* This can never happen: */
2906 			print_error(pos,
2907 			    gettext("Internal error in show.\n"));
2908 			exit(1);
2909 		}
2910 
2911 		if (stat < 0) {
2912 			print_error(pos,
2913 			    gettext("Request for %.3s not sent (%s)\n"),
2914 			    argv[0], idmap_stat2string(handle, stat));
2915 			idmap_get_destroy(ghandle);
2916 			goto cleanup;
2917 		}
2918 
2919 		/* Send the batch to idmapd and obtain results: */
2920 		stat = idmap_get_mappings(ghandle);
2921 		if (stat < 0) {
2922 			print_error(pos,
2923 			    gettext("Mappings not obtained because of"
2924 			    " RPC problem (%s)\n"),
2925 			    idmap_stat2string(handle, stat));
2926 			idmap_get_destroy(ghandle);
2927 			goto cleanup;
2928 		}
2929 
2930 		/* Destroy the batch handle: */
2931 		idmap_get_destroy(ghandle);
2932 
2933 		if (type_to == TYPE_UID)
2934 			nm->pid = uid;
2935 		else if (type_to == TYPE_GID)
2936 			nm->pid = (uid_t)gid;
2937 
2938 	}
2939 
2940 	/*
2941 	 * If there was -c flag, we do output whatever we can even in
2942 	 * the case of error:
2943 	 */
2944 	if (map_stat < 0 && flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
2945 		goto errormsg;
2946 
2947 	/*
2948 	 * idmapd returns fallback uid/gid in case of errors. However
2949 	 * it uses special sentinel value i.e 4294967295 (or -1) to
2950 	 * indicate that falbback pid is not available either. In such
2951 	 * case idmap(1M) should not display the mapping because there
2952 	 * is no fallback mapping.
2953 	 */
2954 
2955 	if ((type_to == TYPE_UID || type_to == TYPE_GID ||
2956 	    type_to == TYPE_PID) && nm->pid == UNDEFINED_UID)
2957 		goto errormsg;
2958 
2959 	if (nm2type(nm, type_from, &fromname) < 0)
2960 		goto errormsg;
2961 
2962 	if (nm2type(nm, type_to, &toname) < 0) {
2963 		if (!(flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC))
2964 			(void) printf("%s -> %s:%u\n",
2965 			    fromname,
2966 			    type_to & IS_GROUP ? ID_GID : ID_UID,
2967 			    UID_NOBODY);
2968 		free(fromname);
2969 	} else {
2970 		(void) printf("%s -> %s\n", fromname, toname);
2971 		free(fromname);
2972 		free(toname);
2973 	}
2974 
2975 errormsg:
2976 	if (map_stat < 0) {
2977 		print_error(pos, gettext("Error:\t%s\n"),
2978 		    idmap_stat2string(handle, map_stat));
2979 		print_error_info(&info);
2980 	} else
2981 		print_info(&info);
2982 	idmap_info_free(&info);
2983 
2984 cleanup:
2985 	if (nm != NULL)
2986 		name_mapping_fini(nm);
2987 	fini_command();
2988 	return (stat < 0 || map_stat < 0 ? -1 : 0);
2989 }
2990 
2991 /* main function. Returns 1 for error, 0 otherwise */
2992 int
2993 main(int argc, char *argv[])
2994 {
2995 	int rc;
2996 
2997 	/* set locale and domain for internationalization */
2998 	(void) setlocale(LC_ALL, "");
2999 	(void) textdomain(TEXT_DOMAIN);
3000 
3001 	/* idmap_engine determines the batch_mode: */
3002 	rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t),
3003 	    commands,
3004 	    argc - 1,
3005 	    argv + 1,
3006 	    &batch_mode);
3007 
3008 	if (rc < 0) {
3009 		(void) engine_fini();
3010 		if (rc == IDMAP_ENG_ERROR_SILENT)
3011 			help();
3012 		return (1);
3013 	}
3014 
3015 	udt_used = 0;
3016 	if (batch_mode) {
3017 		if (init_udt_batch() < 0)
3018 			return (1);
3019 	}
3020 
3021 	rc = run_engine(argc - 1, argv + 1);
3022 
3023 	if (batch_mode) {
3024 		batch_mode = 0;
3025 		if (fini_udt_command(rc == 0 ? 1 : 0, NULL))
3026 			rc = -1;
3027 	}
3028 
3029 	(void) engine_fini();
3030 	return (rc == 0 ? 0 : 1);
3031 }
3032