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