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