d8f5e1b394f2e20911d4bccd96464beb6e250402
[kai/samba.git] / source3 / 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         char *p;
685
686         if (argc < 2 || argv[1][0] == '-') {
687                 usage();
688                 exit(1);
689         }
690         
691         pstrcpy(service, argv[1]);
692         pstrcpy(mpoint, argv[2]);
693
694         /* Convert any '/' characters in the service name to
695            '\' characters */
696         string_replace(service, '/','\\');
697         argc -= 2;
698         argv += 2;
699
700         opt = getopt(argc, argv, "o:");
701         if(opt != 'o') {
702                 return;
703         }
704
705         options[0] = 0;
706         p = options;
707
708         /*
709          * option parsing from nfsmount.c (util-linux-2.9u)
710          */
711         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
712                 DEBUG(3, ("opts: %s\n", opts));
713                 if ((opteq = strchr_m(opts, '='))) {
714                         val = atoi(opteq + 1);
715                         *opteq = '\0';
716
717                         if (!strcmp(opts, "username") || 
718                             !strcmp(opts, "logon")) {
719                                 char *lp;
720                                 pstrcpy(username,opteq+1);
721                                 if ((lp=strchr_m(username,'%'))) {
722                                         *lp = 0;
723                                         pstrcpy(password,lp+1);
724                                         got_pass = True;
725                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
726                                 }
727                                 if ((lp=strchr_m(username,'/'))) {
728                                         *lp = 0;
729                                         pstrcpy(workgroup,lp+1);
730                                 }
731                         } else if(!strcmp(opts, "passwd") ||
732                                   !strcmp(opts, "password")) {
733                                 pstrcpy(password,opteq+1);
734                                 got_pass = True;
735                                 memset(opteq+1,'X',strlen(password));
736                         } else if(!strcmp(opts, "credentials")) {
737                                 pstrcpy(credentials,opteq+1);
738                         } else if(!strcmp(opts, "netbiosname")) {
739                                 pstrcpy(my_netbios_name,opteq+1);
740                         } else if(!strcmp(opts, "uid")) {
741                                 mount_uid = nametouid(opteq+1);
742                         } else if(!strcmp(opts, "gid")) {
743                                 mount_gid = nametogid(opteq+1);
744                         } else if(!strcmp(opts, "port")) {
745                                 smb_port = val;
746                         } else if(!strcmp(opts, "fmask")) {
747                                 mount_fmask = strtol(opteq+1, NULL, 8);
748                         } else if(!strcmp(opts, "dmask")) {
749                                 mount_dmask = strtol(opteq+1, NULL, 8);
750                         } else if(!strcmp(opts, "debug")) {
751                                 DEBUGLEVEL = val;
752                         } else if(!strcmp(opts, "ip")) {
753                                 dest_ip = *interpret_addr2(opteq+1);
754                                 if (is_zero_ip(dest_ip)) {
755                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
756                                         exit(1);
757                                 }
758                                 have_ip = True;
759                         } else if(!strcmp(opts, "workgroup")) {
760                                 pstrcpy(workgroup,opteq+1);
761                         } else if(!strcmp(opts, "sockopt")) {
762                                 pstrcpy(user_socket_options,opteq+1);
763                         } else if(!strcmp(opts, "scope")) {
764                                 pstrcpy(global_scope(),opteq+1);
765                         } else {
766                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
767                                 p += strlen(p);
768                         }
769                 } else {
770                         val = 1;
771                         if(!strcmp(opts, "nocaps")) {
772                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
773                                 exit(1);
774                         } else if(!strcmp(opts, "guest")) {
775                                 *password = '\0';
776                                 got_pass = True;
777                         } else if(!strcmp(opts, "rw")) {
778                                 mount_ro = 0;
779                         } else if(!strcmp(opts, "ro")) {
780                                 mount_ro = 1;
781                         } else {
782                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
783                                 p += strlen(opts);
784                                 *p++ = ',';
785                                 *p = 0;
786                         }
787                 }
788         }
789
790         if (!*service) {
791                 usage();
792                 exit(1);
793         }
794
795         if (p != options) {
796                 *(p-1) = 0;     /* remove trailing , */
797                 DEBUG(3,("passthrough options '%s'\n", options));
798         }
799 }
800
801 /****************************************************************************
802   main program
803 ****************************************************************************/
804  int main(int argc,char *argv[])
805 {
806         extern char *optarg;
807         extern int optind;
808         char *p;
809
810         DEBUGLEVEL = 1;
811
812         /* here we are interactive, even if run from autofs */
813         setup_logging("mount.smbfs",True);
814
815 #if 0 /* JRA - Urban says not needed ? */
816         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
817            is to not announce any unicode capabilities as current smbfs does
818            not support it. */
819         p = getenv("CLI_FORCE_ASCII");
820         if (p && !strcmp(p, "false"))
821                 unsetenv("CLI_FORCE_ASCII");
822         else
823                 setenv("CLI_FORCE_ASCII", "true", 1);
824 #endif
825
826         in_client = True;   /* Make sure that we tell lp_load we are */
827
828         if (getenv("USER")) {
829                 pstrcpy(username,getenv("USER"));
830
831                 if ((p=strchr_m(username,'%'))) {
832                         *p = 0;
833                         pstrcpy(password,p+1);
834                         got_pass = True;
835                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
836                 }
837                 strupper(username);
838         }
839
840         if (getenv("PASSWD")) {
841                 pstrcpy(password,getenv("PASSWD"));
842                 got_pass = True;
843         }
844
845         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
846                 get_password_file();
847                 got_pass = True;
848         }
849
850         if (*username == 0 && getenv("LOGNAME")) {
851                 pstrcpy(username,getenv("LOGNAME"));
852         }
853
854         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
855                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
856                         dyn_CONFIGFILE);
857         }
858
859         parse_mount_smb(argc, argv);
860
861         if (*credentials != 0) {
862                 read_credentials_file(credentials);
863         }
864
865         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
866
867         if (*workgroup == 0) {
868                 pstrcpy(workgroup,lp_workgroup());
869         }
870
871         load_interfaces();
872         if (!*my_netbios_name) {
873                 pstrcpy(my_netbios_name, myhostname());
874         }
875         strupper(my_netbios_name);
876
877         init_mount();
878         return 0;
879 }