a29ab48ecee6c545b5f3768dcc863525fd290899
[tprouty/samba.git] / source / client / smbmount.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMBFS mount program
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #define NO_SYSLOG
22
23 #include "includes.h"
24
25 #include <mntent.h>
26 #include <asm/types.h>
27 #include <linux/smb_fs.h>
28
29 extern BOOL in_client;
30 extern pstring user_socket_options;
31
32 static pstring credentials;
33 static pstring my_netbios_name;
34 static pstring password;
35 static pstring username;
36 static pstring workgroup;
37 static pstring mpoint;
38 static pstring service;
39 static pstring options;
40
41 static struct in_addr dest_ip;
42 static BOOL have_ip;
43 static int smb_port = 0;
44 static BOOL got_pass;
45 static uid_t mount_uid;
46 static gid_t mount_gid;
47 static int mount_ro;
48 static unsigned mount_fmask;
49 static unsigned mount_dmask;
50
51 static void usage(void);
52
53 static void exit_parent(int sig)
54 {
55         /* parent simply exits when child says go... */
56         exit(0);
57 }
58
59 static void daemonize(void)
60 {
61         int j, status;
62         pid_t child_pid;
63
64         signal( SIGTERM, exit_parent );
65
66         if ((child_pid = sys_fork()) < 0) {
67                 DEBUG(0,("could not fork\n"));
68         }
69
70         if (child_pid > 0) {
71                 while( 1 ) {
72                         j = waitpid( child_pid, &status, 0 );
73                         if( j < 0 ) {
74                                 if( EINTR == errno ) {
75                                         continue;
76                                 }
77                                 status = errno;
78                         }
79                         break;
80                 }
81                 /* If we get here - the child exited with some error status */
82                 exit(status);
83         }
84
85         signal( SIGTERM, SIG_DFL );
86         chdir("/");
87 }
88
89 static void close_our_files(int client_fd)
90 {
91         int i;
92         struct rlimit limits;
93
94         getrlimit(RLIMIT_NOFILE,&limits);
95         for (i = 0; i< limits.rlim_max; i++) {
96                 if (i == client_fd)
97                         continue;
98                 close(i);
99         }
100 }
101
102 static void usr1_handler(int x)
103 {
104         return;
105 }
106
107
108 /***************************************************** 
109 return a connection to a server
110 *******************************************************/
111 static struct cli_state *do_connection(char *the_service)
112 {
113         struct cli_state *c;
114         struct nmb_name called, calling;
115         char *server_n;
116         struct in_addr ip;
117         pstring server;
118         char *share;
119
120         if (the_service[0] != '\\' || the_service[1] != '\\') {
121                 usage();
122                 exit(1);
123         }
124
125         pstrcpy(server, the_service+2);
126         share = strchr_m(server,'\\');
127         if (!share) {
128                 usage();
129                 exit(1);
130         }
131         *share = 0;
132         share++;
133
134         server_n = server;
135
136         make_nmb_name(&calling, my_netbios_name, 0x0);
137         make_nmb_name(&called , server, 0x20);
138
139  again:
140         zero_ip(&ip);
141         if (have_ip) ip = dest_ip;
142
143         /* have to open a new connection */
144         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
145             !cli_connect(c, server_n, &ip)) {
146                 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
147                 if (c) {
148                         cli_shutdown(c);
149                 }
150                 return NULL;
151         }
152
153         /* SPNEGO doesn't work till we get NTSTATUS error support */
154         c->use_spnego = False;
155
156         if (!cli_session_request(c, &calling, &called)) {
157                 char *p;
158                 DEBUG(0,("%d: session request to %s failed (%s)\n", 
159                          sys_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", sys_getpid()));
173
174         if (!cli_negprot(c)) {
175                 DEBUG(0,("%d: protocol negotiation failed\n", sys_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                                 sys_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", sys_getpid()));
209
210         if (!cli_send_tconX(c, share, "?????",
211                             password, strlen(password)+1)) {
212                 DEBUG(0,("%d: tree connect failed: %s\n",
213                          sys_getpid(), cli_errstr(c)));
214                 cli_shutdown(c);
215                 return NULL;
216         }
217
218         DEBUG(4,("%d: tconx ok\n", sys_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                          sys_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", sys_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                          sys_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                          sys_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                          sys_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                          sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
293                 return;
294         }
295
296         if (unlink(MOUNTED"~") == -1) {
297                 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_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                                  sys_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                                  sys_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                         set_remote_machine_name("smbmount");    /* sneaky ... */
379                         setup_logging("mount.smbfs", False);
380                         reopen_logs();
381                         DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
382
383                         closed = 1;
384                 }
385
386                 /* Wait for a signal from smbfs ... but don't continue
387                    until we actually get a new connection. */
388                 while (!c) {
389                         CatchSignal(SIGUSR1, &usr1_handler);
390                         pause();
391                         DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
392                         c = do_connection(the_service);
393                 }
394         }
395
396         smb_umount(mount_point);
397         DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
398         exit(1);
399 }
400
401
402 /**
403  * Mount a smbfs
404  **/
405 static void init_mount(void)
406 {
407         char mount_point[MAXPATHLEN+1];
408         pstring tmp;
409         pstring svc2;
410         struct cli_state *c;
411         char *args[20];
412         int i, status;
413
414         if (realpath(mpoint, mount_point) == NULL) {
415                 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
416                 return;
417         }
418
419
420         c = do_connection(service);
421         if (!c) {
422                 fprintf(stderr,"SMB connection failed\n");
423                 exit(1);
424         }
425
426         /*
427                 Set up to return as a daemon child and wait in the parent
428                 until the child say it's ready...
429         */
430         daemonize();
431
432         pstrcpy(svc2, service);
433         string_replace(svc2, '\\','/');
434         string_replace(svc2, ' ','_');
435
436         memset(args, 0, sizeof(args[0])*20);
437
438         i=0;
439         args[i++] = "smbmnt";
440
441         args[i++] = mount_point;
442         args[i++] = "-s";
443         args[i++] = svc2;
444
445         if (mount_ro) {
446                 args[i++] = "-r";
447         }
448         if (mount_uid) {
449                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
450                 args[i++] = "-u";
451                 args[i++] = smb_xstrdup(tmp);
452         }
453         if (mount_gid) {
454                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
455                 args[i++] = "-g";
456                 args[i++] = smb_xstrdup(tmp);
457         }
458         if (mount_fmask) {
459                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
460                 args[i++] = "-f";
461                 args[i++] = smb_xstrdup(tmp);
462         }
463         if (mount_dmask) {
464                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
465                 args[i++] = "-d";
466                 args[i++] = smb_xstrdup(tmp);
467         }
468         if (options) {
469                 args[i++] = "-o";
470                 args[i++] = options;
471         }
472
473         if (sys_fork() == 0) {
474                 char *smbmnt_path;
475
476                 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
477                 
478                 if (file_exist(smbmnt_path, NULL)) {
479                         execv(smbmnt_path, args);
480                         fprintf(stderr,
481                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
482                                 smbmnt_path, strerror(errno));
483                 } else {
484                         execvp("smbmnt", args);
485                         fprintf(stderr,
486                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
487                                 "smbmnt", strerror(errno));
488                 }
489                 free(smbmnt_path);
490                 exit(1);
491         }
492
493         if (waitpid(-1, &status, 0) == -1) {
494                 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
495                 /* FIXME: do some proper error handling */
496                 exit(1);
497         }
498
499         if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
500                 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
501                 /* FIXME: do some proper error handling */
502                 exit(1);
503         }
504
505         /* Ok...  This is the rubicon for that mount point...  At any point
506            after this, if the connections fail and can not be reconstructed
507            for any reason, we will have to unmount the mount point.  There
508            is no exit from the next call...
509         */
510         send_fs_socket(service, mount_point, c);
511 }
512
513
514 /****************************************************************************
515 get a password from a a file or file descriptor
516 exit on failure (from smbclient, move to libsmb or shared .c file?)
517 ****************************************************************************/
518 static void get_password_file(void)
519 {
520         int fd = -1;
521         char *p;
522         BOOL close_it = False;
523         pstring spec;
524         char pass[128];
525
526         if ((p = getenv("PASSWD_FD")) != NULL) {
527                 pstrcpy(spec, "descriptor ");
528                 pstrcat(spec, p);
529                 sscanf(p, "%d", &fd);
530                 close_it = False;
531         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
532                 fd = sys_open(p, O_RDONLY, 0);
533                 pstrcpy(spec, p);
534                 if (fd < 0) {
535                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
536                                 spec, strerror(errno));
537                         exit(1);
538                 }
539                 close_it = True;
540         }
541
542         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
543             p && p - pass < sizeof(pass);) {
544                 switch (read(fd, p, 1)) {
545                 case 1:
546                         if (*p != '\n' && *p != '\0') {
547                                 *++p = '\0'; /* advance p, and null-terminate pass */
548                                 break;
549                         }
550                 case 0:
551                         if (p - pass) {
552                                 *p = '\0'; /* null-terminate it, just in case... */
553                                 p = NULL; /* then force the loop condition to become false */
554                                 break;
555                         } else {
556                                 fprintf(stderr, "Error reading password from file %s: %s\n",
557                                         spec, "empty password\n");
558                                 exit(1);
559                         }
560
561                 default:
562                         fprintf(stderr, "Error reading password from file %s: %s\n",
563                                 spec, strerror(errno));
564                         exit(1);
565                 }
566         }
567         pstrcpy(password, pass);
568         if (close_it)
569                 close(fd);
570 }
571
572 /****************************************************************************
573 get username and password from a credentials file
574 exit on failure (from smbclient, move to libsmb or shared .c file?)
575 ****************************************************************************/
576 static void read_credentials_file(char *filename)
577 {
578         FILE *auth;
579         fstring buf;
580         uint16 len = 0;
581         char *ptr, *val, *param;
582
583         if ((auth=sys_fopen(filename, "r")) == NULL)
584         {
585                 /* fail if we can't open the credentials file */
586                 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
587                 exit (-1);
588         }
589
590         while (!feof(auth))
591         {
592                 /* get a line from the file */
593                 if (!fgets (buf, sizeof(buf), auth))
594                         continue;
595                 len = strlen(buf);
596
597                 if ((len) && (buf[len-1]=='\n'))
598                 {
599                         buf[len-1] = '\0';
600                         len--;
601                 }
602                 if (len == 0)
603                         continue;
604
605                 /* break up the line into parameter & value.
606                    will need to eat a little whitespace possibly */
607                 param = buf;
608                 if (!(ptr = strchr (buf, '=')))
609                         continue;
610                 val = ptr+1;
611                 *ptr = '\0';
612
613                 /* eat leading white space */
614                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
615                         val++;
616
617                 if (strwicmp("password", param) == 0)
618                 {
619                         pstrcpy(password, val);
620                         got_pass = True;
621                 }
622                 else if (strwicmp("username", param) == 0)
623                         pstrcpy(username, val);
624
625                 memset(buf, 0, sizeof(buf));
626         }
627         fclose(auth);
628 }
629
630
631 /****************************************************************************
632 usage on the program
633 ****************************************************************************/
634 static void usage(void)
635 {
636         printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
637
638         printf("Version %s\n\n",VERSION);
639
640         printf(
641 "Options:\n\
642       username=<arg>                  SMB username\n\
643       password=<arg>                  SMB password\n\
644       credentials=<filename>          file with username/password\n\
645       netbiosname=<arg>               source NetBIOS name\n\
646       uid=<arg>                       mount uid or username\n\
647       gid=<arg>                       mount gid or groupname\n\
648       port=<arg>                      remote SMB port number\n\
649       fmask=<arg>                     file umask\n\
650       dmask=<arg>                     directory umask\n\
651       debug=<arg>                     debug level\n\
652       ip=<arg>                        destination host or IP address\n\
653       workgroup=<arg>                 workgroup on destination\n\
654       sockopt=<arg>                   TCP socket options\n\
655       scope=<arg>                     NetBIOS scope\n\
656       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
657       codepage=<arg>                  server codepage (cp850)\n\
658       ttl=<arg>                       dircache time to live\n\
659       guest                           don't prompt for a password\n\
660       ro                              mount read-only\n\
661       rw                              mount read-write\n\
662 \n\
663 This command is designed to be run from within /bin/mount by giving\n\
664 the option '-t smbfs'. For example:\n\
665   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
666 ");
667 }
668
669
670 /****************************************************************************
671   Argument parsing for mount.smbfs interface
672   mount will call us like this:
673     mount.smbfs device mountpoint -o <options>
674   
675   <options> is never empty, containing at least rw or ro
676  ****************************************************************************/
677 static void parse_mount_smb(int argc, char **argv)
678 {
679         int opt;
680         char *opts;
681         char *opteq;
682         extern char *optarg;
683         int val;
684         extern pstring global_scope;
685         char *p;
686
687         if (argc < 2 || argv[1][0] == '-') {
688                 usage();
689                 exit(1);
690         }
691         
692         pstrcpy(service, argv[1]);
693         pstrcpy(mpoint, argv[2]);
694
695         /* Convert any '/' characters in the service name to
696            '\' characters */
697         string_replace(service, '/','\\');
698         argc -= 2;
699         argv += 2;
700
701         opt = getopt(argc, argv, "o:");
702         if(opt != 'o') {
703                 return;
704         }
705
706         options[0] = 0;
707         p = options;
708
709         /*
710          * option parsing from nfsmount.c (util-linux-2.9u)
711          */
712         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
713                 DEBUG(3, ("opts: %s\n", opts));
714                 if ((opteq = strchr_m(opts, '='))) {
715                         val = atoi(opteq + 1);
716                         *opteq = '\0';
717
718                         if (!strcmp(opts, "username") || 
719                             !strcmp(opts, "logon")) {
720                                 char *lp;
721                                 pstrcpy(username,opteq+1);
722                                 if ((lp=strchr_m(username,'%'))) {
723                                         *lp = 0;
724                                         pstrcpy(password,lp+1);
725                                         got_pass = True;
726                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
727                                 }
728                                 if ((lp=strchr_m(username,'/'))) {
729                                         *lp = 0;
730                                         pstrcpy(workgroup,lp+1);
731                                 }
732                         } else if(!strcmp(opts, "passwd") ||
733                                   !strcmp(opts, "password")) {
734                                 pstrcpy(password,opteq+1);
735                                 got_pass = True;
736                                 memset(opteq+1,'X',strlen(password));
737                         } else if(!strcmp(opts, "credentials")) {
738                                 pstrcpy(credentials,opteq+1);
739                         } else if(!strcmp(opts, "netbiosname")) {
740                                 pstrcpy(my_netbios_name,opteq+1);
741                         } else if(!strcmp(opts, "uid")) {
742                                 mount_uid = nametouid(opteq+1);
743                         } else if(!strcmp(opts, "gid")) {
744                                 mount_gid = nametogid(opteq+1);
745                         } else if(!strcmp(opts, "port")) {
746                                 smb_port = val;
747                         } else if(!strcmp(opts, "fmask")) {
748                                 mount_fmask = strtol(opteq+1, NULL, 8);
749                         } else if(!strcmp(opts, "dmask")) {
750                                 mount_dmask = strtol(opteq+1, NULL, 8);
751                         } else if(!strcmp(opts, "debug")) {
752                                 DEBUGLEVEL = val;
753                         } else if(!strcmp(opts, "ip")) {
754                                 dest_ip = *interpret_addr2(opteq+1);
755                                 if (is_zero_ip(dest_ip)) {
756                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
757                                         exit(1);
758                                 }
759                                 have_ip = True;
760                         } else if(!strcmp(opts, "workgroup")) {
761                                 pstrcpy(workgroup,opteq+1);
762                         } else if(!strcmp(opts, "sockopt")) {
763                                 pstrcpy(user_socket_options,opteq+1);
764                         } else if(!strcmp(opts, "scope")) {
765                                 pstrcpy(global_scope,opteq+1);
766                         } else {
767                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
768                                 p += strlen(p);
769                         }
770                 } else {
771                         val = 1;
772                         if(!strcmp(opts, "nocaps")) {
773                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
774                                 exit(1);
775                         } else if(!strcmp(opts, "guest")) {
776                                 *password = '\0';
777                                 got_pass = True;
778                         } else if(!strcmp(opts, "rw")) {
779                                 mount_ro = 0;
780                         } else if(!strcmp(opts, "ro")) {
781                                 mount_ro = 1;
782                         } else {
783                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
784                                 p += strlen(opts);
785                                 *p++ = ',';
786                                 *p = 0;
787                         }
788                 }
789         }
790
791         if (!*service) {
792                 usage();
793                 exit(1);
794         }
795
796         if (p != options) {
797                 *(p-1) = 0;     /* remove trailing , */
798                 DEBUG(3,("passthrough options '%s'\n", options));
799         }
800 }
801
802 /****************************************************************************
803   main program
804 ****************************************************************************/
805  int main(int argc,char *argv[])
806 {
807         extern char *optarg;
808         extern int optind;
809         char *p;
810
811         DEBUGLEVEL = 1;
812
813         /* here we are interactive, even if run from autofs */
814         setup_logging("mount.smbfs",True);
815
816 #if 0 /* JRA - Urban says not needed ? */
817         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
818            is to not announce any unicode capabilities as current smbfs does
819            not support it. */
820         p = getenv("CLI_FORCE_ASCII");
821         if (p && !strcmp(p, "false"))
822                 unsetenv("CLI_FORCE_ASCII");
823         else
824                 setenv("CLI_FORCE_ASCII", "true", 1);
825 #endif
826
827         in_client = True;   /* Make sure that we tell lp_load we are */
828
829         if (getenv("USER")) {
830                 pstrcpy(username,getenv("USER"));
831
832                 if ((p=strchr_m(username,'%'))) {
833                         *p = 0;
834                         pstrcpy(password,p+1);
835                         got_pass = True;
836                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
837                 }
838                 strupper(username);
839         }
840
841         if (getenv("PASSWD")) {
842                 pstrcpy(password,getenv("PASSWD"));
843                 got_pass = True;
844         }
845
846         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
847                 get_password_file();
848                 got_pass = True;
849         }
850
851         if (*username == 0 && getenv("LOGNAME")) {
852                 pstrcpy(username,getenv("LOGNAME"));
853         }
854
855         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
856                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
857                         dyn_CONFIGFILE);
858         }
859
860         parse_mount_smb(argc, argv);
861
862         if (*credentials != 0) {
863                 read_credentials_file(credentials);
864         }
865
866         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
867
868         if (*workgroup == 0) {
869                 pstrcpy(workgroup,lp_workgroup());
870         }
871
872         load_interfaces();
873         if (!*my_netbios_name) {
874                 pstrcpy(my_netbios_name, myhostname());
875         }
876         strupper(my_netbios_name);
877
878         init_mount();
879         return 0;
880 }