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