One more from waitpid() audit: Correctly interpret waitstatus when a
[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 (WIFSIGNALLED(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         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                                 set_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 }