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