Add FIXME about dodgy argument parser.
[gd/samba-autobuild/.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
82                 /* If we get here - the child exited with some error status */
83                 if (WIFSIGNALED(status))
84                         exit(128 + WTERMSIG(status));
85                 else
86                         exit(WEXITSTATUS(status));
87         }
88
89         signal( SIGTERM, SIG_DFL );
90         chdir("/");
91 }
92
93 static void close_our_files(int client_fd)
94 {
95         int i;
96         struct rlimit limits;
97
98         getrlimit(RLIMIT_NOFILE,&limits);
99         for (i = 0; i< limits.rlim_max; i++) {
100                 if (i == client_fd)
101                         continue;
102                 close(i);
103         }
104 }
105
106 static void usr1_handler(int x)
107 {
108         return;
109 }
110
111
112 /***************************************************** 
113 return a connection to a server
114 *******************************************************/
115 static struct cli_state *do_connection(char *the_service)
116 {
117         struct cli_state *c;
118         struct nmb_name called, calling;
119         char *server_n;
120         struct in_addr ip;
121         pstring server;
122         char *share;
123
124         if (the_service[0] != '\\' || the_service[1] != '\\') {
125                 usage();
126                 exit(1);
127         }
128
129         pstrcpy(server, the_service+2);
130         share = strchr_m(server,'\\');
131         if (!share) {
132                 usage();
133                 exit(1);
134         }
135         *share = 0;
136         share++;
137
138         server_n = server;
139
140         make_nmb_name(&calling, my_netbios_name, 0x0);
141         make_nmb_name(&called , server, 0x20);
142
143  again:
144         zero_ip(&ip);
145         if (have_ip) ip = dest_ip;
146
147         /* have to open a new connection */
148         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
149             !cli_connect(c, server_n, &ip)) {
150                 DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
151                 if (c) {
152                         cli_shutdown(c);
153                 }
154                 return NULL;
155         }
156
157         /* SPNEGO doesn't work till we get NTSTATUS error support */
158         c->use_spnego = False;
159
160         if (!cli_session_request(c, &calling, &called)) {
161                 char *p;
162                 DEBUG(0,("%d: session request to %s failed (%s)\n", 
163                          sys_getpid(), called.name, cli_errstr(c)));
164                 cli_shutdown(c);
165                 if ((p=strchr_m(called.name, '.'))) {
166                         *p = 0;
167                         goto again;
168                 }
169                 if (strcmp(called.name, "*SMBSERVER")) {
170                         make_nmb_name(&called , "*SMBSERVER", 0x20);
171                         goto again;
172                 }
173                 return NULL;
174         }
175
176         DEBUG(4,("%d: session request ok\n", sys_getpid()));
177
178         if (!cli_negprot(c)) {
179                 DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
180                 cli_shutdown(c);
181                 return NULL;
182         }
183
184         if (!got_pass) {
185                 char *pass = getpass("Password: ");
186                 if (pass) {
187                         pstrcpy(password, pass);
188                 }
189         }
190
191         /* This should be right for current smbfs. Future versions will support
192           large files as well as unicode and oplocks. */
193         c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
194                                 CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
195         c->force_dos_errors = True;
196         if (!cli_session_setup(c, username, 
197                                password, strlen(password),
198                                password, strlen(password),
199                                workgroup)) {
200                 /* if a password was not supplied then try again with a
201                         null username */
202                 if (password[0] || !username[0] ||
203                                 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
204                         DEBUG(0,("%d: session setup failed: %s\n",
205                                 sys_getpid(), cli_errstr(c)));
206                         cli_shutdown(c);
207                         return NULL;
208                 }
209                 DEBUG(0,("Anonymous login successful\n"));
210         }
211
212         DEBUG(4,("%d: session setup ok\n", sys_getpid()));
213
214         if (!cli_send_tconX(c, share, "?????",
215                             password, strlen(password)+1)) {
216                 DEBUG(0,("%d: tree connect failed: %s\n",
217                          sys_getpid(), cli_errstr(c)));
218                 cli_shutdown(c);
219                 return NULL;
220         }
221
222         DEBUG(4,("%d: tconx ok\n", sys_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                          sys_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", sys_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                          sys_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                          sys_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                          sys_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                          sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
297                 return;
298         }
299
300         if (unlink(MOUNTED"~") == -1) {
301                 DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_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 *the_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                                  sys_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                                  sys_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                 c = NULL;
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                         set_remote_machine_name("smbmount");    /* sneaky ... */
383                         setup_logging("mount.smbfs", False);
384                         reopen_logs();
385                         DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
386
387                         closed = 1;
388                 }
389
390                 /* Wait for a signal from smbfs ... but don't continue
391                    until we actually get a new connection. */
392                 while (!c) {
393                         CatchSignal(SIGUSR1, &usr1_handler);
394                         pause();
395                         DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
396                         c = do_connection(the_service);
397                 }
398         }
399
400         smb_umount(mount_point);
401         DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
402         exit(1);
403 }
404
405
406 /**
407  * Mount a smbfs
408  **/
409 static void init_mount(void)
410 {
411         char mount_point[MAXPATHLEN+1];
412         pstring tmp;
413         pstring svc2;
414         struct cli_state *c;
415         char *args[20];
416         int i, status;
417
418         if (realpath(mpoint, mount_point) == NULL) {
419                 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
420                 return;
421         }
422
423
424         c = do_connection(service);
425         if (!c) {
426                 fprintf(stderr,"SMB connection failed\n");
427                 exit(1);
428         }
429
430         /*
431                 Set up to return as a daemon child and wait in the parent
432                 until the child say it's ready...
433         */
434         daemonize();
435
436         pstrcpy(svc2, service);
437         string_replace(svc2, '\\','/');
438         string_replace(svc2, ' ','_');
439
440         memset(args, 0, sizeof(args[0])*20);
441
442         i=0;
443         args[i++] = "smbmnt";
444
445         args[i++] = mount_point;
446         args[i++] = "-s";
447         args[i++] = svc2;
448
449         if (mount_ro) {
450                 args[i++] = "-r";
451         }
452         if (mount_uid) {
453                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
454                 args[i++] = "-u";
455                 args[i++] = smb_xstrdup(tmp);
456         }
457         if (mount_gid) {
458                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
459                 args[i++] = "-g";
460                 args[i++] = smb_xstrdup(tmp);
461         }
462         if (mount_fmask) {
463                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
464                 args[i++] = "-f";
465                 args[i++] = smb_xstrdup(tmp);
466         }
467         if (mount_dmask) {
468                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
469                 args[i++] = "-d";
470                 args[i++] = smb_xstrdup(tmp);
471         }
472         if (options) {
473                 args[i++] = "-o";
474                 args[i++] = options;
475         }
476
477         if (sys_fork() == 0) {
478                 char *smbmnt_path;
479
480                 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
481                 
482                 if (file_exist(smbmnt_path, NULL)) {
483                         execv(smbmnt_path, args);
484                         fprintf(stderr,
485                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
486                                 smbmnt_path, strerror(errno));
487                 } else {
488                         execvp("smbmnt", args);
489                         fprintf(stderr,
490                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
491                                 "smbmnt", strerror(errno));
492                 }
493                 free(smbmnt_path);
494                 exit(1);
495         }
496
497         if (waitpid(-1, &status, 0) == -1) {
498                 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
499                 /* FIXME: do some proper error handling */
500                 exit(1);
501         }
502
503         if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
504                 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
505                 /* FIXME: do some proper error handling */
506                 exit(1);
507         }
508
509         /* Ok...  This is the rubicon for that mount point...  At any point
510            after this, if the connections fail and can not be reconstructed
511            for any reason, we will have to unmount the mount point.  There
512            is no exit from the next call...
513         */
514         send_fs_socket(service, mount_point, c);
515 }
516
517
518 /****************************************************************************
519 get a password from a a file or file descriptor
520 exit on failure (from smbclient, move to libsmb or shared .c file?)
521 ****************************************************************************/
522 static void get_password_file(void)
523 {
524         int fd = -1;
525         char *p;
526         BOOL close_it = False;
527         pstring spec;
528         char pass[128];
529
530         if ((p = getenv("PASSWD_FD")) != NULL) {
531                 pstrcpy(spec, "descriptor ");
532                 pstrcat(spec, p);
533                 sscanf(p, "%d", &fd);
534                 close_it = False;
535         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
536                 fd = sys_open(p, O_RDONLY, 0);
537                 pstrcpy(spec, p);
538                 if (fd < 0) {
539                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
540                                 spec, strerror(errno));
541                         exit(1);
542                 }
543                 close_it = True;
544         }
545
546         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
547             p && p - pass < sizeof(pass);) {
548                 switch (read(fd, p, 1)) {
549                 case 1:
550                         if (*p != '\n' && *p != '\0') {
551                                 *++p = '\0'; /* advance p, and null-terminate pass */
552                                 break;
553                         }
554                 case 0:
555                         if (p - pass) {
556                                 *p = '\0'; /* null-terminate it, just in case... */
557                                 p = NULL; /* then force the loop condition to become false */
558                                 break;
559                         } else {
560                                 fprintf(stderr, "Error reading password from file %s: %s\n",
561                                         spec, "empty password\n");
562                                 exit(1);
563                         }
564
565                 default:
566                         fprintf(stderr, "Error reading password from file %s: %s\n",
567                                 spec, strerror(errno));
568                         exit(1);
569                 }
570         }
571         pstrcpy(password, pass);
572         if (close_it)
573                 close(fd);
574 }
575
576 /****************************************************************************
577 get username and password from a credentials file
578 exit on failure (from smbclient, move to libsmb or shared .c file?)
579 ****************************************************************************/
580 static void read_credentials_file(char *filename)
581 {
582         FILE *auth;
583         fstring buf;
584         uint16 len = 0;
585         char *ptr, *val, *param;
586
587         if ((auth=sys_fopen(filename, "r")) == NULL)
588         {
589                 /* fail if we can't open the credentials file */
590                 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
591                 exit (-1);
592         }
593
594         while (!feof(auth))
595         {
596                 /* get a line from the file */
597                 if (!fgets (buf, sizeof(buf), auth))
598                         continue;
599                 len = strlen(buf);
600
601                 if ((len) && (buf[len-1]=='\n'))
602                 {
603                         buf[len-1] = '\0';
604                         len--;
605                 }
606                 if (len == 0)
607                         continue;
608
609                 /* break up the line into parameter & value.
610                    will need to eat a little whitespace possibly */
611                 param = buf;
612                 if (!(ptr = strchr (buf, '=')))
613                         continue;
614                 val = ptr+1;
615                 *ptr = '\0';
616
617                 /* eat leading white space */
618                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
619                         val++;
620
621                 if (strwicmp("password", param) == 0)
622                 {
623                         pstrcpy(password, val);
624                         got_pass = True;
625                 }
626                 else if (strwicmp("username", param) == 0)
627                         pstrcpy(username, val);
628
629                 memset(buf, 0, sizeof(buf));
630         }
631         fclose(auth);
632 }
633
634
635 /****************************************************************************
636 usage on the program
637 ****************************************************************************/
638 static void usage(void)
639 {
640         printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
641
642         printf("Version %s\n\n",VERSION);
643
644         printf(
645 "Options:\n\
646       username=<arg>                  SMB username\n\
647       password=<arg>                  SMB password\n\
648       credentials=<filename>          file with username/password\n\
649       netbiosname=<arg>               source NetBIOS name\n\
650       uid=<arg>                       mount uid or username\n\
651       gid=<arg>                       mount gid or groupname\n\
652       port=<arg>                      remote SMB port number\n\
653       fmask=<arg>                     file umask\n\
654       dmask=<arg>                     directory umask\n\
655       debug=<arg>                     debug level\n\
656       ip=<arg>                        destination host or IP address\n\
657       workgroup=<arg>                 workgroup on destination\n\
658       sockopt=<arg>                   TCP socket options\n\
659       scope=<arg>                     NetBIOS scope\n\
660       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
661       codepage=<arg>                  server codepage (cp850)\n\
662       ttl=<arg>                       dircache time to live\n\
663       guest                           don't prompt for a password\n\
664       ro                              mount read-only\n\
665       rw                              mount read-write\n\
666 \n\
667 This command is designed to be run from within /bin/mount by giving\n\
668 the option '-t smbfs'. For example:\n\
669   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
670 ");
671 }
672
673
674 /****************************************************************************
675   Argument parsing for mount.smbfs interface
676   mount will call us like this:
677     mount.smbfs device mountpoint -o <options>
678   
679   <options> is never empty, containing at least rw or ro
680  ****************************************************************************/
681 static void parse_mount_smb(int argc, char **argv)
682 {
683         int opt;
684         char *opts;
685         char *opteq;
686         extern char *optarg;
687         int val;
688         char *p;
689
690         /* FIXME: This function can silently fail if the arguments are
691          * not in the expected order.
692
693         > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
694         > requires that one gives "-o" before further options like username=...
695         > . Without -o, the username=.. setting is *silently* ignored. I've
696         > spent about an hour trying to find out why I couldn't log in now..
697
698         */
699
700
701         if (argc < 2 || argv[1][0] == '-') {
702                 usage();
703                 exit(1);
704         }
705         
706         pstrcpy(service, argv[1]);
707         pstrcpy(mpoint, argv[2]);
708
709         /* Convert any '/' characters in the service name to
710            '\' characters */
711         string_replace(service, '/','\\');
712         argc -= 2;
713         argv += 2;
714
715         opt = getopt(argc, argv, "o:");
716         if(opt != 'o') {
717                 return;
718         }
719
720         options[0] = 0;
721         p = options;
722
723         /*
724          * option parsing from nfsmount.c (util-linux-2.9u)
725          */
726         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
727                 DEBUG(3, ("opts: %s\n", opts));
728                 if ((opteq = strchr_m(opts, '='))) {
729                         val = atoi(opteq + 1);
730                         *opteq = '\0';
731
732                         if (!strcmp(opts, "username") || 
733                             !strcmp(opts, "logon")) {
734                                 char *lp;
735                                 pstrcpy(username,opteq+1);
736                                 if ((lp=strchr_m(username,'%'))) {
737                                         *lp = 0;
738                                         pstrcpy(password,lp+1);
739                                         got_pass = True;
740                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
741                                 }
742                                 if ((lp=strchr_m(username,'/'))) {
743                                         *lp = 0;
744                                         pstrcpy(workgroup,lp+1);
745                                 }
746                         } else if(!strcmp(opts, "passwd") ||
747                                   !strcmp(opts, "password")) {
748                                 pstrcpy(password,opteq+1);
749                                 got_pass = True;
750                                 memset(opteq+1,'X',strlen(password));
751                         } else if(!strcmp(opts, "credentials")) {
752                                 pstrcpy(credentials,opteq+1);
753                         } else if(!strcmp(opts, "netbiosname")) {
754                                 pstrcpy(my_netbios_name,opteq+1);
755                         } else if(!strcmp(opts, "uid")) {
756                                 mount_uid = nametouid(opteq+1);
757                         } else if(!strcmp(opts, "gid")) {
758                                 mount_gid = nametogid(opteq+1);
759                         } else if(!strcmp(opts, "port")) {
760                                 smb_port = val;
761                         } else if(!strcmp(opts, "fmask")) {
762                                 mount_fmask = strtol(opteq+1, NULL, 8);
763                         } else if(!strcmp(opts, "dmask")) {
764                                 mount_dmask = strtol(opteq+1, NULL, 8);
765                         } else if(!strcmp(opts, "debug")) {
766                                 DEBUGLEVEL = val;
767                         } else if(!strcmp(opts, "ip")) {
768                                 dest_ip = *interpret_addr2(opteq+1);
769                                 if (is_zero_ip(dest_ip)) {
770                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
771                                         exit(1);
772                                 }
773                                 have_ip = True;
774                         } else if(!strcmp(opts, "workgroup")) {
775                                 pstrcpy(workgroup,opteq+1);
776                         } else if(!strcmp(opts, "sockopt")) {
777                                 pstrcpy(user_socket_options,opteq+1);
778                         } else if(!strcmp(opts, "scope")) {
779                                 set_global_scope(opteq+1);
780                         } else {
781                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
782                                 p += strlen(p);
783                         }
784                 } else {
785                         val = 1;
786                         if(!strcmp(opts, "nocaps")) {
787                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
788                                 exit(1);
789                         } else if(!strcmp(opts, "guest")) {
790                                 *password = '\0';
791                                 got_pass = True;
792                         } else if(!strcmp(opts, "rw")) {
793                                 mount_ro = 0;
794                         } else if(!strcmp(opts, "ro")) {
795                                 mount_ro = 1;
796                         } else {
797                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
798                                 p += strlen(opts);
799                                 *p++ = ',';
800                                 *p = 0;
801                         }
802                 }
803         }
804
805         if (!*service) {
806                 usage();
807                 exit(1);
808         }
809
810         if (p != options) {
811                 *(p-1) = 0;     /* remove trailing , */
812                 DEBUG(3,("passthrough options '%s'\n", options));
813         }
814 }
815
816 /****************************************************************************
817   main program
818 ****************************************************************************/
819  int main(int argc,char *argv[])
820 {
821         extern char *optarg;
822         extern int optind;
823         char *p;
824
825         DEBUGLEVEL = 1;
826
827         /* here we are interactive, even if run from autofs */
828         setup_logging("mount.smbfs",True);
829
830 #if 0 /* JRA - Urban says not needed ? */
831         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
832            is to not announce any unicode capabilities as current smbfs does
833            not support it. */
834         p = getenv("CLI_FORCE_ASCII");
835         if (p && !strcmp(p, "false"))
836                 unsetenv("CLI_FORCE_ASCII");
837         else
838                 setenv("CLI_FORCE_ASCII", "true", 1);
839 #endif
840
841         in_client = True;   /* Make sure that we tell lp_load we are */
842
843         if (getenv("USER")) {
844                 pstrcpy(username,getenv("USER"));
845
846                 if ((p=strchr_m(username,'%'))) {
847                         *p = 0;
848                         pstrcpy(password,p+1);
849                         got_pass = True;
850                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
851                 }
852                 strupper(username);
853         }
854
855         if (getenv("PASSWD")) {
856                 pstrcpy(password,getenv("PASSWD"));
857                 got_pass = True;
858         }
859
860         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
861                 get_password_file();
862                 got_pass = True;
863         }
864
865         if (*username == 0 && getenv("LOGNAME")) {
866                 pstrcpy(username,getenv("LOGNAME"));
867         }
868
869         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
870                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
871                         dyn_CONFIGFILE);
872         }
873
874         parse_mount_smb(argc, argv);
875
876         if (*credentials != 0) {
877                 read_credentials_file(credentials);
878         }
879
880         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
881
882         if (*workgroup == 0) {
883                 pstrcpy(workgroup,lp_workgroup());
884         }
885
886         load_interfaces();
887         if (!*my_netbios_name) {
888                 pstrcpy(my_netbios_name, myhostname());
889         }
890         strupper(my_netbios_name);
891
892         init_mount();
893         return 0;
894 }