xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/fksmbcl/fksmbcl_main.c (revision 8329232e00f1048795bae53acb230316243aadb5)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Test & debug program for the SMB client
18  *
19  * This implements a simple command reader which accepts
20  * commands to simulate system calls into the file system.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/file.h>
25 #include <sys/stat.h>
26 #include <sys/mount.h>
27 #include <sys/dirent.h>
28 #include <sys/strlog.h>		/* SL_NOTE */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 
38 #include <sys/fs/smbfs_mount.h>
39 #include <netsmb/smb_lib.h>
40 #include <libfknsmb/common/libfknsmb.h>
41 #include <libfksmbfs/common/libfksmbfs.h>
42 
43 #if _FILE_OFFSET_BITS != 64
44 #error "This calls (fake) VFS code which requires 64-bit off_t"
45 #endif
46 
47 extern int list_shares(struct smb_ctx *);
48 
49 #define	MAXARG	10
50 
51 struct cmd_tbl_ent {
52 	void (*ce_func)(int, char **);
53 	const char *ce_name;
54 	const char *ce_argdesc;
55 };
56 static struct cmd_tbl_ent cmd_tbl[];
57 
58 static struct smb_ctx *ctx = NULL;
59 static char *server = NULL;
60 
61 static vfs_t *vfsp = NULL;
62 
63 static void show_dents(vnode_t *, offset_t *, char *, int);
64 static void run_cli(void);
65 
66 #define	TBUFSZ 8192
67 static char tbuf[TBUFSZ];
68 
69 static void
70 fksmbcl_usage(void)
71 {
72 	printf("usage: fksmbcl //user@server (like smbutil)\n");
73 	exit(1);
74 }
75 
76 int
77 main(int argc, char *argv[])
78 {
79 	int error, opt;
80 
81 	/*
82 	 * Initializations
83 	 */
84 	nsmb_drv_load();
85 	nsmb_drv_init();
86 	fksmbfs_init();
87 
88 	while ((opt = getopt(argc, argv, "dv")) != -1) {
89 		switch (opt) {
90 		case 'd':
91 			smb_debug++;
92 			break;
93 		case 'v':
94 			smb_verbose++;
95 			break;
96 		case '?':
97 			fksmbcl_usage();
98 			break;
99 		}
100 	}
101 	if (optind >= argc)
102 		fksmbcl_usage();
103 	server = argv[optind];
104 
105 	/*
106 	 * Setup the libsmbfs context
107 	 */
108 	error = smb_ctx_alloc(&ctx);
109 	if (error) {
110 		fprintf(stderr, "%s: smb_ctx_alloc failed (%d)\n",
111 		    argv[0], error);
112 		return (1);
113 	}
114 
115 	error = smb_ctx_scan_argv(ctx, argc, argv,
116 	    SMBL_SERVER, SMBL_SERVER, USE_WILDCARD);
117 	if (error) {
118 		fprintf(stderr, "logon: smb_ctx_scan_argv, error %d\n", error);
119 		return (1);
120 	}
121 	error = smb_ctx_readrc(ctx);
122 	if (error) {
123 		fprintf(stderr, "logon: smb_ctx_readrc, error %d\n", error);
124 		return (1);
125 	}
126 
127 	/* Do smb_ctx_setshare later, and smb_ctx_resolve. */
128 
129 	/*
130 	 * Next would be smb_ctx_get_ssn() but don't do that until
131 	 * the "logon" command so one can set breakpoints etc.
132 	 */
133 
134 	/*
135 	 * Run the CLI
136 	 */
137 	run_cli();
138 
139 	/*
140 	 * Cleanup
141 	 */
142 	fksmbfs_fini();
143 	nsmb_drv_fini();
144 
145 	return (0);
146 }
147 
148 static void
149 run_cli()
150 {
151 	static char cmdbuf[100];
152 	int argc, i;
153 	char *argv[MAXARG];
154 	char *cmd;
155 	char *savep = NULL;
156 	char *sep = " \t\n";
157 	char *prompt = NULL;
158 	struct cmd_tbl_ent *ce;
159 
160 	if (isatty(0)) {
161 		fputs("# Start with:\n"
162 		    "> logon [user [dom [pw]]]\n"
163 		    "> shares\n"
164 		    "> mount {share}\n\n",
165 		    stdout);
166 		prompt = "> ";
167 	}
168 
169 	for (;;) {
170 		if (prompt) {
171 			fputs(prompt, stdout);
172 			fflush(stdout);
173 		}
174 
175 		cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
176 		if (cmd == NULL)
177 			break;
178 		if (cmd[0] == '#')
179 			continue;
180 
181 		if (prompt == NULL) {
182 			/* Put commands in the output too. */
183 			fprintf(stdout, "+ %s", cmdbuf);
184 		}
185 
186 		argv[0] = strtok_r(cmd, sep, &savep);
187 		if (argv[0] == NULL)
188 			continue;
189 		for (argc = 1; argc < MAXARG; argc++) {
190 			argv[argc] = strtok_r(NULL, sep, &savep);
191 			if (argv[argc] == NULL)
192 				break;
193 		}
194 		for (i = argc; i < MAXARG; i++)
195 			argv[i++] = NULL;
196 
197 		for (ce = cmd_tbl; ce->ce_name != NULL; ce++)
198 			if (strcmp(ce->ce_name, argv[0]) == 0)
199 				break;
200 		if (ce->ce_name != NULL) {
201 			ce->ce_func(argc, argv);
202 		} else {
203 			fprintf(stderr, "%s unknown command. Try help\n",
204 			    argv[0]);
205 		}
206 	}
207 }
208 
209 /*
210  * Command handlers
211  */
212 
213 static void
214 do_exit(int argc, char **argv)
215 {
216 	exit(0);
217 }
218 
219 static void
220 do_help(int argc, char **argv)
221 {
222 	struct cmd_tbl_ent *ce;
223 
224 	printf("Commands:\n");
225 	for (ce = cmd_tbl; ce->ce_func != NULL; ce++)
226 		printf("%s %s\n", ce->ce_name, ce->ce_argdesc);
227 }
228 
229 static void
230 do_logon(int argc, char **argv)
231 {
232 	int error;
233 
234 	if (argc > 1) {
235 		if (argv[1][0] == '-') {
236 			smb_ctx_setuser(ctx, "", B_TRUE);
237 			ctx->ct_flags |= SMBCF_NOPWD;
238 		} else {
239 			smb_ctx_setuser(ctx, argv[1], B_TRUE);
240 		}
241 	}
242 	if (argc > 2)
243 		smb_ctx_setdomain(ctx, argv[2], B_TRUE);
244 	if (argc > 3)
245 		smb_ctx_setpassword(ctx, argv[3], NULL);
246 
247 	/*
248 	 * Resolve the server address, setup derived defaults.
249 	 */
250 	error = smb_ctx_resolve(ctx);
251 	if (error) {
252 		fprintf(stderr, "logon: smb_ctx_resolve, error %d\n", error);
253 		return;
254 	}
255 
256 	/*
257 	 * Have server, share, etc. now.
258 	 * Get the logon session.
259 	 */
260 again:
261 	error = smb_ctx_get_ssn(ctx);
262 	if (error == EAUTH) {
263 		int err2;
264 		err2 = smb_get_authentication(ctx);
265 		if (err2 == 0)
266 			goto again;
267 	}
268 	if (error) {
269 		fprintf(stderr, "//%s: login failed, error %d\n",
270 		    ctx->ct_fullserver, error);
271 	}
272 }
273 
274 /*
275  * Drop session created by the "logon" command.
276  */
277 static void
278 do_logoff(int argc, char **argv)
279 {
280 
281 	(void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_SSN_RELE, NULL);
282 	if (argc > 1) {
283 		smb_ctx_done(ctx);
284 		(void) smb_ctx_init(ctx);
285 	}
286 }
287 
288 /*
289  * List shares
290  */
291 static void
292 do_shares(int argc, char **argv)
293 {
294 	int error;
295 
296 	smb_ctx_setshare(ctx, "IPC$", USE_IPC);
297 	error = smb_ctx_get_tree(ctx);
298 	if (error) {
299 		fprintf(stderr, "shares, tcon IPC$, error=%d\n", error);
300 		return;
301 	}
302 
303 	error = list_shares(ctx);
304 	if (error) {
305 		fprintf(stderr, "shares, enum, error=%d\n", error);
306 	}
307 
308 	(void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL);
309 }
310 
311 char mnt_opt_buf[MAX_MNTOPT_STR];
312 char mnt_resource[MAXPATHLEN];
313 
314 /*
315  * Minimal excerpt from vfs.c:domount()
316  */
317 void
318 do_mount(int argc, char **argv)
319 {
320 	struct smbfs_args mdata;
321 	struct mounta ma;
322 	char *shrname;
323 	int error;
324 
325 	if (vfsp != NULL) {
326 		fprintf(stderr, "Already mounted\n");
327 		return;
328 	}
329 
330 	if (argc < 2) {
331 		fprintf(stderr, "%s: missing share name\n", argv[0]);
332 		return;
333 	}
334 	shrname = argv[1];
335 	if (argc > 2)
336 		strlcpy(mnt_opt_buf, argv[2], sizeof (mnt_opt_buf));
337 	else
338 		memset(mnt_opt_buf, 0, sizeof (mnt_opt_buf));
339 
340 	smb_ctx_setshare(ctx, shrname, USE_DISKDEV);
341 	error = smb_ctx_get_tree(ctx);
342 	if (error) {
343 		fprintf(stderr, "//%s/%s: tree connect failed, %d\n",
344 		    server, shrname, error);
345 		return;
346 	}
347 
348 	(void) snprintf(mnt_resource, sizeof (mnt_resource),
349 	    "//%s/%s", ctx->ct_fullserver, shrname);
350 
351 	bzero(&mdata, sizeof (mdata));
352 	mdata.version = SMBFS_VERSION;		/* smbfs mount version */
353 	mdata.file_mode = S_IRWXU;
354 	mdata.dir_mode = S_IRWXU;
355 	mdata.devfd = ctx->ct_dev_fd;
356 
357 	/* Build mount args */
358 	bzero(&ma, sizeof (ma));
359 	ma.spec = mnt_resource;
360 	ma.dir = "/";
361 	ma.flags =  MS_DATA | MS_OPTIONSTR | MS_NOSPLICE | MS_NOSUID;
362 	ma.fstype = "smbfs";
363 	ma.dataptr = (void *) &mdata;
364 	ma.datalen = sizeof (mdata);
365 	ma.optptr = mnt_opt_buf;
366 	ma.optlen = sizeof (mnt_opt_buf);
367 
368 	error = fake_domount("smbfs", &ma, &vfsp);
369 	if (error != 0) {
370 		fprintf(stderr, "domount error=%d\n", error);
371 	}
372 
373 	/* Mount takes a ref, so always rele here. */
374 	(void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL);
375 }
376 
377 void
378 do_unmount(int argc, char **argv)
379 {
380 	int error;
381 
382 	if (vfsp == NULL) {
383 		fprintf(stderr, "Not mounted\n");
384 		return;
385 	}
386 
387 	error = fake_dounmount(vfsp, 0);
388 	if (error != 0) {
389 		fprintf(stderr, "dounmount error=%d\n", error);
390 		return;
391 	}
392 	vfsp = NULL;
393 }
394 
395 void
396 do_statfs(int argc, char **argv)
397 {
398 	statvfs64_t st;
399 	int error;
400 
401 	if (vfsp == NULL) {
402 		fprintf(stderr, "Not mounted\n");
403 		return;
404 	}
405 
406 	error = fsop_statfs(vfsp, &st);
407 	if (error != 0) {
408 		fprintf(stderr, "df error=%d\n", error);
409 		return;
410 	}
411 	printf("bsize=%ld\n", st.f_bsize);
412 	printf("frsize=%ld\n", st.f_frsize);
413 	printf("blocks=%" PRIu64 "\n", st.f_blocks);
414 	printf(" bfree=%" PRIu64 "\n", st.f_bfree);
415 	printf("bavail=%" PRIu64 "\n", st.f_bavail);
416 }
417 
418 void
419 do_dir(int argc, char **argv)
420 {
421 	char *rdir;
422 	vnode_t *vp = NULL;
423 	offset_t off;
424 	int cnt;
425 	int error;
426 
427 	if (vfsp == NULL) {
428 		fprintf(stderr, "mnt required first\n");
429 		return;
430 	}
431 	if (argc > 1)
432 		rdir = argv[1];
433 	else
434 		rdir = "";
435 
436 	error = vn_open(rdir, 0, FREAD, 0, &vp, 0, 0);
437 	if (error != 0) {
438 		fprintf(stderr, "do_dir, vn_open error=%d\n", error);
439 		return;
440 	}
441 
442 	off = 0;
443 	do {
444 		cnt = fake_getdents(vp, &off, tbuf, TBUFSZ);
445 		if (cnt < 0) {
446 			fprintf(stderr, "do_dir, getdents %d\n", -cnt);
447 			break;
448 		}
449 		show_dents(vp, &off, tbuf, cnt);
450 	} while (cnt > 0);
451 
452 	if (vp != NULL)
453 		vn_close_rele(vp, 0);
454 }
455 
456 void
457 do_dirx(int argc, char **argv)
458 {
459 	char *rdir;
460 	vnode_t *vp = NULL;
461 	offset_t off;
462 	int cnt;
463 	int error;
464 
465 	if (vfsp == NULL) {
466 		fprintf(stderr, "mnt required first\n");
467 		return;
468 	}
469 	if (argc > 1)
470 		rdir = argv[1];
471 	else
472 		rdir = "";
473 
474 	error = vn_open(rdir, 0, FREAD|FXATTRDIROPEN, 0, &vp, 0, 0);
475 	if (error != 0) {
476 		fprintf(stderr, "do_dirx, vn_open error=%d\n", error);
477 		return;
478 	}
479 
480 	off = 0;
481 	do {
482 		cnt = fake_getdents(vp, &off, tbuf, TBUFSZ);
483 		if (cnt < 0) {
484 			fprintf(stderr, "do_dirx, getdents %d\n", -cnt);
485 			break;
486 		}
487 		show_dents(vp, &off, tbuf, cnt);
488 	} while (cnt > 0);
489 
490 	if (vp != NULL)
491 		vn_close_rele(vp, 0);
492 }
493 
494 static void
495 show_dents(vnode_t *dvp, offset_t *offp, char *buf, int cnt)
496 {
497 	char time_buf[40];
498 	struct stat64 st;
499 	vnode_t *vp;
500 	char *p;
501 	dirent_t *d;
502 	offset_t offset = (offset_t)-1L;
503 	int error;
504 	uint_t mode;
505 	char type;
506 
507 	p = buf;
508 	while (p < (buf + cnt)) {
509 		d = (dirent_t *)(void *)p;
510 		p += d->d_reclen;
511 		offset = d->d_off;
512 
513 		error = fake_lookup(dvp, d->d_name, &vp);
514 		if (error != 0) {
515 			fprintf(stderr, "%s: lookup error=%d\n",
516 			    d->d_name, error);
517 			continue;
518 		}
519 		error = fake_stat(vp, &st, 0);
520 		vn_rele(vp);
521 		if (error != 0) {
522 			fprintf(stderr, "%s: stat error=%d\n",
523 			    d->d_name, error);
524 			continue;
525 		}
526 
527 		/*
528 		 * Print type, mode, size, name
529 		 * First mode (only dir, file expected here)
530 		 */
531 		if (S_ISDIR(st.st_mode)) {
532 			type = 'd';
533 		} else if (S_ISREG(st.st_mode)) {
534 			type = ' ';
535 		} else {
536 			type = '?';
537 		}
538 		mode = st.st_mode & 0777;
539 		(void) strftime(time_buf, sizeof (time_buf),
540 		    "%b %e %T %Y", localtime(&st.st_mtime));
541 
542 		printf("%c 0%3o %9" PRIu64 "  %s  %s\n",
543 		    type, mode,
544 		    (uint64_t)st.st_size,
545 		    time_buf,
546 		    d->d_name);
547 	}
548 	*offp = offset;
549 }
550 
551 /*
552  * get rname [lname]
553  */
554 void
555 do_get(int argc, char **argv)
556 {
557 	struct stat64 st;
558 	char *rname;
559 	char *lname;
560 	vnode_t *vp = NULL;
561 	offset_t off;
562 	ssize_t cnt, x;
563 	int oflg = O_RDWR | O_CREAT;
564 	int lfd = -1;
565 	int error;
566 
567 	if (vfsp == NULL) {
568 		fprintf(stderr, "mnt required first\n");
569 		return;
570 	}
571 	if (argc < 2) {
572 		fprintf(stderr, "rname required\n");
573 		return;
574 	}
575 	rname = argv[1];
576 	if (argc > 2) {
577 		lname = argv[2];
578 		/*
579 		 * When local name is specified, overwrite.
580 		 * Convenient for scripts etc.
581 		 */
582 		oflg |= O_TRUNC;
583 	} else {
584 		lname = rname;
585 		/* Local file should not exist. */
586 		oflg |= O_EXCL;
587 	}
588 
589 	lfd = open(lname, oflg, 0644);
590 	if (lfd < 0) {
591 		perror(lname);
592 		return;
593 	}
594 
595 	error = vn_open(rname, 0, FREAD, 0, &vp, 0, 0);
596 	if (error != 0) {
597 		fprintf(stderr, "do_get, vn_open error=%d\n", error);
598 		goto out;
599 	}
600 	error = fake_stat(vp, &st, 0);
601 	if (error != 0) {
602 		fprintf(stderr, "do_get, stat error=%d\n", error);
603 		goto out;
604 	}
605 
606 	off = 0;
607 	do {
608 		cnt = fake_pread(vp, tbuf, TBUFSZ, off);
609 		if (cnt < 0) {
610 			fprintf(stderr, "do_get, read %d\n", -cnt);
611 			goto out;
612 		}
613 		x = write(lfd, tbuf, cnt);
614 		if (x < 0) {
615 			fprintf(stderr, "do_get, write %d\n", errno);
616 			goto out;
617 		}
618 		off += x;
619 	} while (off < st.st_size);
620 
621 out:
622 	if (vp != NULL)
623 		vn_close_rele(vp, 0);
624 	if (lfd != -1)
625 		close(lfd);
626 }
627 
628 /*
629  * put lname [rname]
630  */
631 void
632 do_put(int argc, char **argv)
633 {
634 	struct stat64 rst;
635 	struct stat st;
636 	char *rname;
637 	char *lname;
638 	vnode_t *vp = NULL;
639 	offset_t off;
640 	ssize_t cnt, x;
641 	int oflg = FREAD|FWRITE|FCREAT;
642 	int lfd = -1;
643 	int error;
644 
645 	if (vfsp == NULL) {
646 		fprintf(stderr, "mnt required first\n");
647 		return;
648 	}
649 	if (argc < 2) {
650 		fprintf(stderr, "lname required\n");
651 		return;
652 	}
653 	lname = argv[1];
654 	if (argc > 2) {
655 		rname = argv[2];
656 		/*
657 		 * When remote name is specified, overwrite.
658 		 * Convenient for scripts etc.
659 		 */
660 		oflg |= FTRUNC;
661 	} else {
662 		rname = lname;
663 		/* Remote file should not exist. */
664 		oflg |= FEXCL;
665 	}
666 
667 	lfd = open(lname, O_RDONLY, 0);
668 	if (lfd < 0) {
669 		perror(lname);
670 		return;
671 	}
672 	error = fstat(lfd, &st);
673 	if (error != 0) {
674 		fprintf(stderr, "do_put, stat error=%d\n", error);
675 		goto out;
676 	}
677 
678 	error = vn_open(rname, 0, oflg, 0, &vp, 0, 0);
679 	if (error != 0) {
680 		fprintf(stderr, "do_put, vn_open error=%d\n", error);
681 		goto out;
682 	}
683 
684 	off = 0;
685 	do {
686 		cnt = pread(lfd, tbuf, TBUFSZ, off);
687 		if (cnt < 0) {
688 			fprintf(stderr, "do_put, read %d\n", errno);
689 			goto out;
690 		}
691 		x = fake_pwrite(vp, tbuf, cnt, off);
692 		if (x < 0) {
693 			fprintf(stderr, "do_put, write %d\n", -x);
694 			goto out;
695 		}
696 		off += cnt;
697 	} while (off < st.st_size);
698 
699 	/* This getattr should go OtW. */
700 	error = fake_stat(vp, &rst, 0);
701 	if (error != 0) {
702 		fprintf(stderr, "do_put, stat error=%d\n", error);
703 		goto out;
704 	}
705 	if (rst.st_size != st.st_size) {
706 		fprintf(stderr, "do_put, wrong size?\n");
707 	}
708 
709 out:
710 	if (vp != NULL)
711 		vn_close_rele(vp, 0);
712 	if (lfd != -1)
713 		close(lfd);
714 }
715 
716 /*
717  * rm rname
718  */
719 void
720 do_rm(int argc, char **argv)
721 {
722 	char *rname;
723 	int error;
724 
725 	if (vfsp == NULL) {
726 		fprintf(stderr, "mnt required first\n");
727 		return;
728 	}
729 	if (argc < 2) {
730 		fprintf(stderr, "rname required\n");
731 		return;
732 	}
733 	rname = argv[1];
734 
735 	error = fake_unlink(rname, 0);
736 	if (error != 0) {
737 		fprintf(stderr, "do_rm, unlink error=%d\n", error);
738 	}
739 }
740 
741 /*
742  * mv fromname toname
743  */
744 void
745 do_mv(int argc, char **argv)
746 {
747 	int error;
748 
749 	if (vfsp == NULL) {
750 		fprintf(stderr, "mnt required first\n");
751 		return;
752 	}
753 	if (argc < 3) {
754 		fprintf(stderr, "from_name to_name required\n");
755 		return;
756 	}
757 
758 	error = fake_rename(argv[1], argv[2]);
759 	if (error != 0) {
760 		fprintf(stderr, "do_mv, rename error=%d\n", error);
761 	}
762 }
763 
764 /*
765  * mkdir rname
766  */
767 void
768 do_mkdir(int argc, char **argv)
769 {
770 	char *rname;
771 	vnode_t *vp = NULL;
772 	int error;
773 
774 	if (vfsp == NULL) {
775 		fprintf(stderr, "mnt required first\n");
776 		return;
777 	}
778 	if (argc < 2) {
779 		fprintf(stderr, "rname required\n");
780 		return;
781 	}
782 	rname = argv[1];
783 
784 	error = vn_open(rname, 0, FCREAT, 0, &vp, CRMKDIR, 0);
785 	if (error != 0) {
786 		fprintf(stderr, "do_mkdir, vn_open error=%d\n", error);
787 	}
788 
789 	if (vp != NULL)
790 		vn_close_rele(vp, 0);
791 }
792 
793 /*
794  * rmdir rname
795  */
796 void
797 do_rmdir(int argc, char **argv)
798 {
799 	char *rname;
800 	int error;
801 
802 	if (vfsp == NULL) {
803 		fprintf(stderr, "mnt required first\n");
804 		return;
805 	}
806 	if (argc < 2) {
807 		fprintf(stderr, "rname required\n");
808 		return;
809 	}
810 	rname = argv[1];
811 
812 	error = fake_unlink(rname, AT_REMOVEDIR);
813 	if (error != 0) {
814 		fprintf(stderr, "do_rmdir, unlink error=%d\n", error);
815 	}
816 }
817 
818 /*
819  * Simple option setting
820  *
821  * Each arg is handled as one line in .nsmbrc [default]
822  */
823 void
824 do_opt(int argc, char **argv)
825 {
826 	static char template[20] = "/tmp/fksmbclXXXXXX";
827 	static char rcname[30];
828 	char *tdname;
829 	char *save_home;
830 	FILE *fp;
831 	int err, i;
832 
833 	if (argc < 2) {
834 		fprintf(stderr, "opt {key}[=value]\n");
835 		return;
836 	}
837 
838 	tdname = mkdtemp(template);
839 	if (tdname == NULL) {
840 		perror("mkdtemp");
841 		return;
842 	}
843 	(void) snprintf(rcname, sizeof (rcname),
844 	    "%s/.nsmbrc", tdname);
845 
846 	fp = fopen(rcname, "w");
847 	if (fp == NULL) {
848 		perror(rcname);
849 		goto out;
850 	}
851 	fprintf(fp, "[default]\n");
852 	for (i = 1; i < argc; i++)
853 		fprintf(fp, "%s\n", argv[i]);
854 	fclose(fp);
855 
856 	save_home = ctx->ct_home;
857 	ctx->ct_home = tdname;
858 	err = smb_ctx_readrc(ctx);
859 	ctx->ct_home = save_home;
860 
861 	if (err != 0)
862 		fprintf(stderr, "readrc, err=%d\n", err);
863 
864 out:
865 	(void) unlink(rcname);
866 	(void) rmdir(tdname);
867 }
868 
869 /*
870  * Command table
871  */
872 static struct cmd_tbl_ent
873 cmd_tbl[] = {
874 	{ do_help,	"help", "" },
875 	{ do_exit,	"exit", "" },
876 	{ do_logon,	"logon", "[user [dom [pass]]]" },
877 	{ do_logoff,	"logoff", "[close-driver]" },
878 	{ do_shares,	"shares", "" },
879 	{ do_mount,	"mount",  "{share} [optstr]" },
880 	{ do_unmount,	"umount", "" },
881 	{ do_unmount,	"unmount", "" },
882 	{ do_statfs,	"statfs", "" },
883 	{ do_dir,	"dir",  "{rdir} [lfile]" },
884 	{ do_dirx,	"dirx", "{rdir} [lfile]" },
885 	{ do_get,	"get",  "{rfile} [lfile]" },
886 	{ do_put,	"put",  "{lfile} [rfile]" },
887 	{ do_mv,	"mv",   "{from} {to}" },
888 	{ do_rm,	"rm",   "{rfile}" },
889 	{ do_mkdir,	"mkdir", "{rfile}" },
890 	{ do_rmdir,	"rmdir", "{rfile}" },
891 	{ do_opt,	"opt",   "{option}" },
892 	{ NULL, NULL, NULL }
893 };
894 
895 /*
896  * Provide a real function (one that prints something) to replace
897  * the stub in libfakekernel.  This prints cmn_err() messages.
898  */
899 void
900 fakekernel_putlog(char *msg, size_t len, int flags)
901 {
902 
903 	/*
904 	 * [CE_CONT, CE_NOTE, CE_WARN, CE_PANIC] maps to
905 	 * [SL_NOTE, SL_NOTE, SL_WARN, SL_FATAL]
906 	 */
907 	if (smb_debug == 0 && (flags & SL_NOTE))
908 		return;
909 	(void) fwrite(msg, 1, len, stdout);
910 	(void) fflush(stdout);
911 }
912 
913 /*
914  * Enable libumem debugging
915  */
916 const char *
917 _umem_debug_init(void)
918 {
919 	return ("default,verbose"); /* $UMEM_DEBUG setting */
920 }
921 
922 const char *
923 _umem_logging_init(void)
924 {
925 	return ("fail,contents"); /* $UMEM_LOGGING setting */
926 }
927