1/*
2   Unix SMB/Netbios implementation.
3   Version 2.0.
4   SMBFS mount program
5   Copyright (C) Andrew Tridgell 1999
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22#define NO_SYSLOG
23
24#include "includes.h"
25
26#include <mntent.h>
27#include <asm/types.h>
28#include <linux/smb_fs.h>
29
30extern struct in_addr ipzero;
31extern int DEBUGLEVEL;
32extern BOOL in_client;
33extern pstring user_socket_options;
34extern BOOL append_log;
35extern fstring remote_machine;
36
37static pstring credentials;
38static pstring my_netbios_name;
39static pstring password;
40static pstring username;
41static pstring workgroup;
42static pstring mpoint;
43static pstring service;
44static pstring options;
45
46static struct in_addr dest_ip;
47static BOOL have_ip;
48static int smb_port = 139;
49static BOOL got_pass;
50static uid_t mount_uid;
51static gid_t mount_gid;
52static int mount_ro;
53static unsigned mount_fmask;
54static unsigned mount_dmask;
55
56static void usage(void);
57
58static void exit_parent(int sig)
59{
60	/* parent simply exits when child says go... */
61	exit(0);
62}
63
64static void daemonize(void)
65{
66	int j, status;
67	pid_t child_pid;
68
69	signal( SIGTERM, exit_parent );
70
71	if ((child_pid = fork()) < 0) {
72		DEBUG(0,("could not fork\n"));
73	}
74
75	if (child_pid > 0) {
76		while( 1 ) {
77			j = waitpid( child_pid, &status, 0 );
78			if( j < 0 ) {
79				if( EINTR == errno ) {
80					continue;
81				}
82				status = errno;
83			}
84			break;
85		}
86		/* If we get here - the child exited with some error status */
87		exit(status);
88	}
89
90	signal( SIGTERM, SIG_DFL );
91	chdir("/");
92}
93
94static void close_our_files(int client_fd)
95{
96	int i;
97	struct rlimit limits;
98
99	getrlimit(RLIMIT_NOFILE,&limits);
100	for (i = 0; i< limits.rlim_max; i++) {
101		if (i == client_fd)
102			continue;
103		close(i);
104	}
105}
106
107static void usr1_handler(int x)
108{
109	return;
110}
111
112
113/*****************************************************
114return a connection to a server
115*******************************************************/
116
117static struct cli_state *do_connection(char *svc_name)
118{
119	struct cli_state *c;
120	struct nmb_name called, calling;
121	char *server_n;
122	struct in_addr ip;
123	pstring server;
124	char *share;
125
126	if (svc_name[0] != '\\' || svc_name[1] != '\\') {
127		usage();
128		exit(1);
129	}
130
131	pstrcpy(server, svc_name+2);
132	share = strchr(server,'\\');
133	if (!share) {
134		usage();
135		exit(1);
136	}
137	*share = 0;
138	share++;
139
140	server_n = server;
141
142	ip = ipzero;
143
144	make_nmb_name(&calling, my_netbios_name, 0x0);
145	make_nmb_name(&called , server, 0x20);
146
147 again:
148	ip = ipzero;
149	if (have_ip) ip = dest_ip;
150
151	/* have to open a new connection */
152	if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) == 0) ||
153	    !cli_connect(c, server_n, &ip)) {
154		DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
155		if (c) {
156			cli_shutdown(c);
157		}
158		return NULL;
159	}
160
161	if (!cli_session_request(c, &calling, &called)) {
162		char *p;
163		DEBUG(0,("%d: session request to %s failed (%s)\n",
164			 getpid(), called.name, cli_errstr(c)));
165		cli_shutdown(c);
166		if ((p=strchr(called.name, '.'))) {
167			*p = 0;
168			goto again;
169		}
170		if (strcmp(called.name, "*SMBSERVER")) {
171			make_nmb_name(&called , "*SMBSERVER", 0x20);
172			goto again;
173		}
174		return NULL;
175	}
176
177	DEBUG(4,("%d: session request ok\n", getpid()));
178
179	if (!cli_negprot(c)) {
180		DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
181		cli_shutdown(c);
182		return NULL;
183	}
184
185	if (!got_pass) {
186		char *pass = getpass("Password: ");
187		if (pass) {
188			pstrcpy(password, pass);
189		}
190	}
191
192	/* This should be right for current smbfs. Future versions will support
193	   large files as well as unicode and oplocks. */
194	c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
195			     CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
196	if (!cli_session_setup(c, username,
197			       password, strlen(password),
198			       password, strlen(password),
199			       workgroup)) {
200		/* if a password was not supplied then try again with a
201		   null username */
202		if (password[0] || !username[0] ||
203		    !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
204			DEBUG(0,("%d: session setup failed: %s\n",
205				 getpid(), cli_errstr(c)));
206			cli_shutdown(c);
207			return NULL;
208		}
209		DEBUG(0,("Anonymous login successful\n"));
210	}
211
212	DEBUG(4,("%d: session setup ok\n", getpid()));
213
214	if (!cli_send_tconX(c, share, "?????",
215			    password, strlen(password)+1)) {
216		DEBUG(0,("%d: tree connect failed: %s\n",
217			 getpid(), cli_errstr(c)));
218		cli_shutdown(c);
219		return NULL;
220	}
221
222	DEBUG(4,("%d: tconx ok\n", getpid()));
223
224	got_pass = True;
225
226	return c;
227}
228
229
230/****************************************************************************
231unmount smbfs  (this is a bailout routine to clean up if a reconnect fails)
232	Code blatently stolen from smbumount.c
233		-mhw-
234****************************************************************************/
235static void smb_umount(char *mount_point)
236{
237	int fd;
238        struct mntent *mnt;
239        FILE* mtab;
240        FILE* new_mtab;
241
242	/* Programmers Note:
243		This routine only gets called to the scene of a disaster
244		to shoot the survivors...  A connection that was working
245		has now apparently failed.  We have an active mount point
246		(presumably) that we need to dump.  If we get errors along
247		the way - make some noise, but we are already turning out
248		the lights to exit anyways...
249	*/
250        if (umount(mount_point) != 0) {
251                DEBUG(0,("%d: Could not umount %s: %s\n",
252			 getpid(), mount_point, strerror(errno)));
253                return;
254        }
255
256        if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
257                DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
258                return;
259        }
260
261        close(fd);
262
263        if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
264                DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
265			 getpid(), strerror(errno)));
266                return;
267        }
268
269#define MOUNTED_TMP MOUNTED".tmp"
270
271        if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
272                DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
273			 getpid(), strerror(errno)));
274                endmntent(mtab);
275                return;
276        }
277
278        while ((mnt = getmntent(mtab)) != NULL) {
279                if (strcmp(mnt->mnt_dir, mount_point) != 0) {
280                        addmntent(new_mtab, mnt);
281                }
282        }
283
284        endmntent(mtab);
285
286        if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
287                DEBUG(0,("%d: Error changing mode of %s: %s\n",
288			 getpid(), MOUNTED_TMP, strerror(errno)));
289                return;
290        }
291
292        endmntent(new_mtab);
293
294        if (rename(MOUNTED_TMP, MOUNTED) < 0) {
295                DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
296			 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
297                return;
298        }
299
300        if (unlink(MOUNTED"~") == -1) {
301                DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
302                return;
303        }
304}
305
306
307/*
308 * Call the smbfs ioctl to install a connection socket,
309 * then wait for a signal to reconnect. Note that we do
310 * not exit after open_sockets() or send_login() errors,
311 * as the smbfs mount would then have no way to recover.
312 */
313static void send_fs_socket(char *svc_name, char *mount_point, struct cli_state *c)
314{
315	int fd, closed = 0, res = 1;
316	pid_t parentpid = getppid();
317	struct smb_conn_opt conn_options;
318
319	memset(&conn_options, 0, sizeof(conn_options));
320
321	while (1) {
322		if ((fd = open(mount_point, O_RDONLY)) < 0) {
323			DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
324				 getpid(), mount_point));
325			break;
326		}
327
328		conn_options.fd = c->fd;
329		conn_options.protocol = c->protocol;
330		conn_options.case_handling = SMB_CASE_DEFAULT;
331		conn_options.max_xmit = c->max_xmit;
332		conn_options.server_uid = c->vuid;
333		conn_options.tid = c->cnum;
334		conn_options.secmode = c->sec_mode;
335		conn_options.rawmode = 0;
336		conn_options.sesskey = c->sesskey;
337		conn_options.maxraw = 0;
338		conn_options.capabilities = c->capabilities;
339		conn_options.serverzone = c->serverzone/60;
340
341		res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
342		if (res != 0) {
343			DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
344				 getpid(), res));
345			close(fd);
346			break;
347		}
348
349		if (parentpid) {
350			/* Ok...  We are going to kill the parent.  Now
351				is the time to break the process group... */
352			setsid();
353			/* Send a signal to the parent to terminate */
354			kill(parentpid, SIGTERM);
355			parentpid = 0;
356		}
357
358		close(fd);
359
360		/* This looks wierd but we are only closing the userspace
361		   side, the connection has already been passed to smbfs and
362		   it has increased the usage count on the socket.
363
364		   If we don't do this we will "leak" sockets and memory on
365		   each reconnection we have to make. */
366		cli_shutdown(c);
367		c = NULL;
368
369		if (!closed) {
370			/* redirect stdout & stderr since we can't know that
371			   the library functions we use are using DEBUG. */
372			if ( (fd = open("/dev/null", O_WRONLY)) < 0)
373				DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
374			close_our_files(fd);
375			if (fd >= 0) {
376				dup2(fd, STDOUT_FILENO);
377				dup2(fd, STDERR_FILENO);
378				close(fd);
379			}
380
381			/* here we are no longer interactive */
382			pstrcpy(remote_machine, "smbmount");	/* sneaky ... */
383			setup_logging("mount.smbfs", False);
384			append_log = True;
385			reopen_logs();
386			DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", svc_name, getpid()));
387
388			closed = 1;
389		}
390
391		/* Wait for a signal from smbfs ... but don't continue
392                   until we actually get a new connection. */
393		while (!c) {
394			CatchSignal(SIGUSR1, &usr1_handler);
395			pause();
396			DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
397			c = do_connection(svc_name);
398		}
399	}
400
401	smb_umount(mount_point);
402	DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
403	exit(1);
404}
405
406/*********************************************************
407a strdup with exit
408**********************************************************/
409static char *xstrdup(char *s)
410{
411	s = strdup(s);
412	if (!s) {
413		fprintf(stderr,"out of memory\n");
414		exit(1);
415	}
416	return s;
417}
418
419
420/****************************************************************************
421mount smbfs
422****************************************************************************/
423static void init_mount(void)
424{
425	char mount_point[MAXPATHLEN+1];
426	pstring tmp;
427	pstring svc2;
428	struct cli_state *c;
429	char *args[20];
430	int i, status;
431
432	if (realpath(mpoint, mount_point) == NULL) {
433		fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
434		return;
435	}
436
437
438	c = do_connection(service);
439	if (!c) {
440		fprintf(stderr,"SMB connection failed\n");
441		exit(1);
442	}
443
444	/*
445		Set up to return as a daemon child and wait in the parent
446		until the child say it's ready...
447	*/
448	daemonize();
449
450	pstrcpy(svc2, service);
451	string_replace(svc2, '\\','/');
452	string_replace(svc2, ' ','_');
453
454	memset(args, 0, sizeof(args[0])*20);
455
456	i=0;
457	args[i++] = "smbmnt";
458
459	args[i++] = mount_point;
460	args[i++] = "-s";
461	args[i++] = svc2;
462
463	if (mount_ro) {
464		args[i++] = "-r";
465	}
466	if (mount_uid) {
467		slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
468		args[i++] = "-u";
469		args[i++] = xstrdup(tmp);
470	}
471	if (mount_gid) {
472		slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
473		args[i++] = "-g";
474		args[i++] = xstrdup(tmp);
475	}
476	if (mount_fmask) {
477		slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
478		args[i++] = "-f";
479		args[i++] = xstrdup(tmp);
480	}
481	if (mount_dmask) {
482		slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
483		args[i++] = "-d";
484		args[i++] = xstrdup(tmp);
485	}
486	if (options) {
487		args[i++] = "-o";
488		args[i++] = options;
489	}
490
491	if (fork() == 0) {
492		if (file_exist(BINDIR "/smbmnt", NULL)) {
493			execv(BINDIR "/smbmnt", args);
494			fprintf(stderr,"execv of %s failed. Error was %s.", BINDIR "/smbmnt", strerror(errno));
495		} else {
496			execvp("smbmnt", args);
497			fprintf(stderr,"execvp of smbmnt failed. Error was %s.", strerror(errno) );
498		}
499		exit(1);
500	}
501
502	if (waitpid(-1, &status, 0) == -1) {
503		fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
504		/* FIXME: do some proper error handling */
505		exit(1);
506	}
507
508	if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
509		fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
510		/* FIXME: do some proper error handling */
511		exit(1);
512	}
513
514	/* Ok...  This is the rubicon for that mount point...  At any point
515	   after this, if the connections fail and can not be reconstructed
516	   for any reason, we will have to unmount the mount point.  There
517	   is no exit from the next call...
518	*/
519	send_fs_socket(service, mount_point, c);
520}
521
522
523/****************************************************************************
524get a password from a a file or file descriptor
525exit on failure (from smbclient, move to libsmb or shared .c file?)
526****************************************************************************/
527static void get_password_file(void)
528{
529	int fd = -1;
530	char *p;
531	BOOL close_it = False;
532	pstring spec;
533	char pass[128];
534
535	if ((p = getenv("PASSWD_FD")) != NULL) {
536		pstrcpy(spec, "descriptor ");
537		pstrcat(spec, p);
538		sscanf(p, "%d", &fd);
539		close_it = False;
540	} else if ((p = getenv("PASSWD_FILE")) != NULL) {
541		fd = sys_open(p, O_RDONLY, 0);
542		pstrcpy(spec, p);
543		if (fd < 0) {
544			fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
545				spec, strerror(errno));
546			exit(1);
547		}
548		close_it = True;
549	}
550
551	for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
552	    p && p - pass < sizeof(pass);) {
553		switch (read(fd, p, 1)) {
554		case 1:
555			if (*p != '\n' && *p != '\0') {
556				*++p = '\0'; /* advance p, and null-terminate pass */
557				break;
558			}
559		case 0:
560			if (p - pass) {
561				*p = '\0'; /* null-terminate it, just in case... */
562				p = NULL; /* then force the loop condition to become false */
563				break;
564			} else {
565				fprintf(stderr, "Error reading password from file %s: %s\n",
566					spec, "empty password\n");
567				exit(1);
568			}
569
570		default:
571			fprintf(stderr, "Error reading password from file %s: %s\n",
572				spec, strerror(errno));
573			exit(1);
574		}
575	}
576	pstrcpy(password, pass);
577	if (close_it)
578		close(fd);
579}
580
581/****************************************************************************
582get username and password from a credentials file
583exit on failure (from smbclient, move to libsmb or shared .c file?)
584****************************************************************************/
585static void read_credentials_file(char *filename)
586{
587	FILE *auth;
588	fstring buf;
589	uint16 len = 0;
590	char *ptr, *val, *param;
591
592	if ((auth=sys_fopen(filename, "r")) == NULL)
593	{
594		/* fail if we can't open the credentials file */
595		DEBUG(0,("ERROR: Unable to open credentials file!\n"));
596		exit (-1);
597	}
598
599	while (!feof(auth))
600	{
601		/* get a line from the file */
602		if (!fgets (buf, sizeof(buf), auth))
603			continue;
604		len = strlen(buf);
605
606		if ((len) && (buf[len-1]=='\n'))
607		{
608			buf[len-1] = '\0';
609			len--;
610		}
611		if (len == 0)
612			continue;
613
614		/* break up the line into parameter & value.
615		   will need to eat a little whitespace possibly */
616		param = buf;
617		if (!(ptr = strchr (buf, '=')))
618			continue;
619		val = ptr+1;
620		*ptr = '\0';
621
622		/* eat leading white space */
623		while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
624			val++;
625
626		if (strstr(param, "password") == 0)
627		{
628			pstrcpy(password, val);
629			got_pass = True;
630		}
631		else if (strstr(param, "username") == 0)
632			pstrcpy(username, val);
633
634		memset(buf, 0, sizeof(buf));
635	}
636	fclose(auth);
637}
638
639
640/****************************************************************************
641usage on the program
642****************************************************************************/
643static void usage(void)
644{
645	printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
646
647	printf("Version %s\n\n",VERSION);
648
649	printf(
650"Options:\n\
651      username=<arg>                  SMB username\n\
652      password=<arg>                  SMB password\n\
653      credentials=<filename>          file with username/password\n\
654      netbiosname=<arg>               source NetBIOS name\n\
655      uid=<arg>                       mount uid or username\n\
656      gid=<arg>                       mount gid or groupname\n\
657      port=<arg>                      remote SMB port number\n\
658      fmask=<arg>                     file umask\n\
659      dmask=<arg>                     directory umask\n\
660      debug=<arg>                     debug level\n\
661      ip=<arg>                        destination host or IP address\n\
662      workgroup=<arg>                 workgroup on destination\n\
663      sockopt=<arg>                   TCP socket options\n\
664      scope=<arg>                     NetBIOS scope\n\
665      iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
666      codepage=<arg>                  server codepage (cp850)\n\
667      ttl=<arg>                       dircache time to live\n\
668      guest                           don't prompt for a password\n\
669      ro                              mount read-only\n\
670      rw                              mount read-write\n\
671\n\
672This command is designed to be run from within /bin/mount by giving\n\
673the option '-t smbfs'. For example:\n\
674  mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
675");
676}
677
678
679/****************************************************************************
680  Argument parsing for mount.smbfs interface
681  mount will call us like this:
682    mount.smbfs device mountpoint -o <options>
683
684  <options> is never empty, containing at least rw or ro
685 ****************************************************************************/
686static void parse_mount_smb(int argc, char **argv)
687{
688	int opt;
689	char *opts;
690	char *opteq;
691	extern char *optarg;
692	int val;
693	extern pstring global_scope;
694	char *p;
695
696	if (argc < 2 || argv[1][0] == '-') {
697		usage();
698		exit(1);
699	}
700
701	pstrcpy(service, argv[1]);
702	pstrcpy(mpoint, argv[2]);
703
704	/* Convert any '/' characters in the service name to
705	   '\' characters */
706	string_replace(service, '/','\\');
707	argc -= 2;
708	argv += 2;
709
710	opt = getopt(argc, argv, "o:");
711	if(opt != 'o') {
712		return;
713	}
714
715	options[0] = 0;
716	p = options;
717
718	/*
719	 * option parsing from nfsmount.c (util-linux-2.9u)
720	 */
721        for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
722		DEBUG(3, ("opts: %s\n", opts));
723                if ((opteq = strchr(opts, '='))) {
724                        val = atoi(opteq + 1);
725                        *opteq = '\0';
726
727                        if (!strcmp(opts, "username") ||
728			    !strcmp(opts, "logon")) {
729				char *lp;
730				pstrcpy(username,opteq+1);
731				if ((lp=strchr(username,'%'))) {
732					*lp = 0;
733					pstrcpy(password,lp+1);
734					got_pass = True;
735					memset(strchr(opteq+1,'%')+1,'X',strlen(password));
736				}
737				if ((lp=strchr(username,'/'))) {
738					*lp = 0;
739					pstrcpy(workgroup,lp+1);
740				}
741			} else if(!strcmp(opts, "passwd") ||
742				  !strcmp(opts, "password")) {
743				pstrcpy(password,opteq+1);
744				got_pass = True;
745				memset(opteq+1,'X',strlen(password));
746			} else if(!strcmp(opts, "credentials")) {
747				pstrcpy(credentials,opteq+1);
748			} else if(!strcmp(opts, "netbiosname")) {
749				pstrcpy(my_netbios_name,opteq+1);
750			} else if(!strcmp(opts, "uid")) {
751				mount_uid = nametouid(opteq+1);
752			} else if(!strcmp(opts, "gid")) {
753				mount_gid = nametogid(opteq+1);
754			} else if(!strcmp(opts, "port")) {
755				smb_port = val;
756			} else if(!strcmp(opts, "fmask")) {
757				mount_fmask = strtol(opteq+1, NULL, 8);
758			} else if(!strcmp(opts, "dmask")) {
759				mount_dmask = strtol(opteq+1, NULL, 8);
760			} else if(!strcmp(opts, "debug")) {
761				DEBUGLEVEL = val;
762			} else if(!strcmp(opts, "ip")) {
763				dest_ip = *interpret_addr2(opteq+1);
764				if (zero_ip(dest_ip)) {
765					fprintf(stderr,"Can't resolve address %s\n", opteq+1);
766					exit(1);
767				}
768				have_ip = True;
769			} else if(!strcmp(opts, "workgroup")) {
770				pstrcpy(workgroup,opteq+1);
771			} else if(!strcmp(opts, "sockopt")) {
772				pstrcpy(user_socket_options,opteq+1);
773			} else if(!strcmp(opts, "scope")) {
774				pstrcpy(global_scope,opteq+1);
775			} else {
776				slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
777				p += strlen(p);
778			}
779		} else {
780			val = 1;
781			if(!strcmp(opts, "nocaps")) {
782				fprintf(stderr, "Unhandled option: %s\n", opteq+1);
783				exit(1);
784			} else if(!strcmp(opts, "guest")) {
785				*password = '\0';
786				got_pass = True;
787			} else if(!strcmp(opts, "rw")) {
788				mount_ro = 0;
789			} else if(!strcmp(opts, "ro")) {
790				mount_ro = 1;
791			} else {
792				strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
793				p += strlen(opts);
794				*p++ = ',';
795				*p = 0;
796			}
797		}
798	}
799
800	if (!*service) {
801		usage();
802		exit(1);
803	}
804
805	if (p != options) {
806		*(p-1) = 0;	/* remove trailing , */
807		DEBUG(3,("passthrough options '%s'\n", options));
808	}
809}
810
811/****************************************************************************
812  main program
813****************************************************************************/
814 int main(int argc,char *argv[])
815{
816	extern char *optarg;
817	extern int optind;
818	static pstring servicesf = CONFIGFILE;
819	char *p;
820
821	DEBUGLEVEL = 1;
822
823	/* here we are interactive, even if run from autofs */
824	setup_logging("mount.smbfs",True);
825
826	TimeInit();
827	charset_initialise();
828
829	in_client = True;   /* Make sure that we tell lp_load we are */
830
831	if (getenv("USER")) {
832		pstrcpy(username,getenv("USER"));
833
834		if ((p=strchr(username,'%'))) {
835			*p = 0;
836			pstrcpy(password,p+1);
837			got_pass = True;
838			memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
839		}
840		strupper(username);
841	}
842
843	if (getenv("PASSWD")) {
844		pstrcpy(password,getenv("PASSWD"));
845		got_pass = True;
846	}
847
848	if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
849		get_password_file();
850		got_pass = True;
851	}
852
853	if (*username == 0 && getenv("LOGNAME")) {
854		pstrcpy(username,getenv("LOGNAME"));
855	}
856
857	if (!lp_load(servicesf,True,False,False)) {
858		fprintf(stderr, "Can't load %s - run testparm to debug it\n",
859			servicesf);
860	}
861
862	parse_mount_smb(argc, argv);
863
864	if (*credentials != 0) {
865		read_credentials_file(credentials);
866	}
867
868	DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
869
870	codepage_initialise(lp_client_code_page());
871
872	if (*workgroup == 0) {
873		pstrcpy(workgroup,lp_workgroup());
874	}
875
876	load_interfaces();
877	if (!*my_netbios_name) {
878		pstrcpy(my_netbios_name, myhostname());
879	}
880	strupper(my_netbios_name);
881
882	init_mount();
883	return 0;
884}
885