xref: /illumos-gate/usr/src/cmd/idmap/idmap/idmap.c (revision d362b749)
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