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