a couple of minor merges from 2_2
[gd/samba-autobuild/.git] / source3 / client / smbmount.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0.
4    SMBFS mount program
5    Copyright (C) Andrew Tridgell 1999
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #define NO_SYSLOG
23
24 #include "includes.h"
25
26 #include <mntent.h>
27 #include <asm/types.h>
28 #include <linux/smb_fs.h>
29
30 extern struct in_addr ipzero;
31 extern int DEBUGLEVEL;
32
33 extern BOOL in_client;
34 extern pstring user_socket_options;
35 extern BOOL append_log;
36 extern fstring remote_machine;
37
38 static pstring credentials;
39 static pstring my_netbios_name;
40 static pstring password;
41 static pstring username;
42 static pstring workgroup;
43 static pstring mpoint;
44 static pstring service;
45 static pstring options;
46
47 static struct in_addr dest_ip;
48 static BOOL have_ip;
49 static int smb_port = 139;
50 static BOOL got_pass;
51 static uid_t mount_uid;
52 static gid_t mount_gid;
53 static int mount_ro;
54 static unsigned mount_fmask;
55 static unsigned mount_dmask;
56
57 static void usage(void);
58
59 static void exit_parent(int sig)
60 {
61         /* parent simply exits when child says go... */
62         exit(0);
63 }
64
65 static void daemonize(void)
66 {
67         int j, status;
68         pid_t child_pid;
69
70         signal( SIGTERM, exit_parent );
71
72         if ((child_pid = sys_fork()) < 0) {
73                 DEBUG(0,("could not fork\n"));
74         }
75
76         if (child_pid > 0) {
77                 while( 1 ) {
78                         j = waitpid( child_pid, &status, 0 );
79                         if( j < 0 ) {
80                                 if( EINTR == errno ) {
81                                         continue;
82                                 }
83                                 status = errno;
84                         }
85                         break;
86                 }
87                 /* If we get here - the child exited with some error status */
88                 exit(status);
89         }
90
91         signal( SIGTERM, SIG_DFL );
92         chdir("/");
93 }
94
95 static void close_our_files(int client_fd)
96 {
97         int i;
98         struct rlimit limits;
99
100         getrlimit(RLIMIT_NOFILE,&limits);
101         for (i = 0; i< limits.rlim_max; i++) {
102                 if (i == client_fd)
103                         continue;
104                 close(i);
105         }
106 }
107
108 static void usr1_handler(int x)
109 {
110         return;
111 }
112
113
114 /***************************************************** 
115 return a connection to a server
116 *******************************************************/
117 static struct cli_state *do_connection(char *service)
118 {
119         struct cli_state *c;
120         struct nmb_name called, calling;
121         char *server_n;
122         struct in_addr ip;
123         extern struct in_addr ipzero;
124         pstring server;
125         char *share;
126
127         if (service[0] != '\\' || service[1] != '\\') {
128                 usage();
129                 exit(1);
130         }
131
132         pstrcpy(server, service+2);
133         share = strchr(server,'\\');
134         if (!share) {
135                 usage();
136                 exit(1);
137         }
138         *share = 0;
139         share++;
140
141         server_n = server;
142
143         make_nmb_name(&calling, my_netbios_name, 0x0);
144         make_nmb_name(&called , server, 0x20);
145
146  again:
147         ip = ipzero;
148         if (have_ip) ip = dest_ip;
149
150         /* have to open a new connection */
151         if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) == 0) ||
152             !cli_connect(c, server_n, &ip)) {
153                 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
154                 if (c) {
155                         cli_shutdown(c);
156                         free(c);
157                 }
158                 return NULL;
159         }
160
161         if (!cli_session_request(c, &calling, &called)) {
162                 char *p;
163                 DEBUG(0,("%d: session request to %s failed (%s)\n", 
164                          getpid(), called.name, cli_errstr(c)));
165                 cli_shutdown(c);
166                 free(c);
167                 if ((p=strchr(called.name, '.'))) {
168                         *p = 0;
169                         goto again;
170                 }
171                 if (strcmp(called.name, "*SMBSERVER")) {
172                         make_nmb_name(&called , "*SMBSERVER", 0x20);
173                         goto again;
174                 }
175                 return NULL;
176         }
177
178         DEBUG(4,("%d: session request ok\n", getpid()));
179
180         if (!cli_negprot(c)) {
181                 DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
182                 cli_shutdown(c);
183                 free(c);
184                 return NULL;
185         }
186
187         if (!got_pass) {
188                 char *pass = getpass("Password: ");
189                 if (pass) {
190                         pstrcpy(password, pass);
191                 }
192         }
193
194         if (!cli_session_setup(c, username, 
195                                password, strlen(password),
196                                password, strlen(password),
197                                workgroup)) {
198                 DEBUG(0,("%d: session setup failed: %s\n",
199                          getpid(), cli_errstr(c)));
200                 cli_shutdown(c);
201                 free(c);
202                 return NULL;
203         }
204
205         DEBUG(4,("%d: session setup ok\n", getpid()));
206
207         if (!cli_send_tconX(c, share, "?????",
208                             password, strlen(password)+1)) {
209                 DEBUG(0,("%d: tree connect failed: %s\n",
210                          getpid(), cli_errstr(c)));
211                 cli_shutdown(c);
212                 free(c);
213                 return NULL;
214         }
215
216         DEBUG(4,("%d: tconx ok\n", getpid()));
217
218         got_pass = True;
219
220         return c;
221 }
222
223
224 /****************************************************************************
225 unmount smbfs  (this is a bailout routine to clean up if a reconnect fails)
226         Code blatently stolen from smbumount.c
227                 -mhw-
228 ****************************************************************************/
229 static void smb_umount(char *mount_point)
230 {
231         int fd;
232         struct mntent *mnt;
233         FILE* mtab;
234         FILE* new_mtab;
235
236         /* Programmers Note:
237                 This routine only gets called to the scene of a disaster
238                 to shoot the survivors...  A connection that was working
239                 has now apparently failed.  We have an active mount point
240                 (presumably) that we need to dump.  If we get errors along
241                 the way - make some noise, but we are already turning out
242                 the lights to exit anyways...
243         */
244         if (umount(mount_point) != 0) {
245                 DEBUG(0,("%d: Could not umount %s: %s\n",
246                          getpid(), mount_point, strerror(errno)));
247                 return;
248         }
249
250         if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
251                 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
252                 return;
253         }
254
255         close(fd);
256         
257         if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
258                 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
259                          getpid(), strerror(errno)));
260                 return;
261         }
262
263 #define MOUNTED_TMP MOUNTED".tmp"
264
265         if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
266                 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
267                          getpid(), strerror(errno)));
268                 endmntent(mtab);
269                 return;
270         }
271
272         while ((mnt = getmntent(mtab)) != NULL) {
273                 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
274                         addmntent(new_mtab, mnt);
275                 }
276         }
277
278         endmntent(mtab);
279
280         if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
281                 DEBUG(0,("%d: Error changing mode of %s: %s\n",
282                          getpid(), MOUNTED_TMP, strerror(errno)));
283                 return;
284         }
285
286         endmntent(new_mtab);
287
288         if (rename(MOUNTED_TMP, MOUNTED) < 0) {
289                 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
290                          getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
291                 return;
292         }
293
294         if (unlink(MOUNTED"~") == -1) {
295                 DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
296                 return;
297         }
298 }
299
300
301 /*
302  * Call the smbfs ioctl to install a connection socket,
303  * then wait for a signal to reconnect. Note that we do
304  * not exit after open_sockets() or send_login() errors,
305  * as the smbfs mount would then have no way to recover.
306  */
307 static void send_fs_socket(char *service, char *mount_point, struct cli_state *c)
308 {
309         int fd, closed = 0, res = 1;
310         pid_t parentpid = getppid();
311         struct smb_conn_opt conn_options;
312
313         memset(&conn_options, 0, sizeof(conn_options));
314
315         while (1) {
316                 if ((fd = open(mount_point, O_RDONLY)) < 0) {
317                         DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
318                                  getpid(), mount_point));
319                         break;
320                 }
321
322                 conn_options.fd = c->fd;
323                 conn_options.protocol = c->protocol;
324                 conn_options.case_handling = SMB_CASE_DEFAULT;
325                 conn_options.max_xmit = c->max_xmit;
326                 conn_options.server_uid = c->vuid;
327                 conn_options.tid = c->cnum;
328                 conn_options.secmode = c->sec_mode;
329                 conn_options.rawmode = 0;
330                 conn_options.sesskey = c->sesskey;
331                 conn_options.maxraw = 0;
332                 conn_options.capabilities = c->capabilities;
333                 conn_options.serverzone = c->serverzone/60;
334
335                 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
336                 if (res != 0) {
337                         DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
338                                  getpid(), res));
339                         close(fd);
340                         break;
341                 }
342
343                 if (parentpid) {
344                         /* Ok...  We are going to kill the parent.  Now
345                                 is the time to break the process group... */
346                         setsid();
347                         /* Send a signal to the parent to terminate */
348                         kill(parentpid, SIGTERM);
349                         parentpid = 0;
350                 }
351
352                 close(fd);
353
354                 /* This looks wierd but we are only closing the userspace
355                    side, the connection has already been passed to smbfs and 
356                    it has increased the usage count on the socket.
357
358                    If we don't do this we will "leak" sockets and memory on
359                    each reconnection we have to make. */
360                 cli_shutdown(c);
361                 free(c);
362                 c = NULL;
363
364                 if (!closed) {
365                         /* redirect stdout & stderr since we can't know that
366                            the library functions we use are using DEBUG. */
367                         if ( (fd = open("/dev/null", O_WRONLY)) < 0)
368                                 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
369                         close_our_files(fd);
370                         if (fd >= 0) {
371                                 dup2(fd, STDOUT_FILENO);
372                                 dup2(fd, STDERR_FILENO);
373                                 close(fd);
374                         }
375
376                         /* here we are no longer interactive */
377                         pstrcpy(remote_machine, "smbmount");    /* sneaky ... */
378                         setup_logging("mount.smbfs", False);
379                         append_log = True;
380                         reopen_logs();
381                         DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", service, getpid()));
382
383                         closed = 1;
384                 }
385
386                 /* Wait for a signal from smbfs ... but don't continue
387                    until we actually get a new connection. */
388                 while (!c) {
389                         CatchSignal(SIGUSR1, &usr1_handler);
390                         pause();
391                         DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
392                         c = do_connection(service);
393                 }
394         }
395
396         smb_umount(mount_point);
397         DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
398         exit(1);
399 }
400
401 /*********************************************************
402 a strdup with exit
403 **********************************************************/
404 static char *xstrdup(char *s)
405 {
406         s = strdup(s);
407         if (!s) {
408                 fprintf(stderr,"out of memory\n");
409                 exit(1);
410         }
411         return s;
412 }
413
414
415 /****************************************************************************
416 mount smbfs
417 ****************************************************************************/
418 static void init_mount(void)
419 {
420         char mount_point[MAXPATHLEN+1];
421         pstring tmp;
422         pstring svc2;
423         struct cli_state *c;
424         char *args[20];
425         int i, status;
426
427         if (realpath(mpoint, mount_point) == NULL) {
428                 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
429                 return;
430         }
431
432
433         c = do_connection(service);
434         if (!c) {
435                 fprintf(stderr,"SMB connection failed\n");
436                 exit(1);
437         }
438
439         /*
440                 Set up to return as a daemon child and wait in the parent
441                 until the child say it's ready...
442         */
443         daemonize();
444
445         pstrcpy(svc2, service);
446         string_replace(svc2, '\\','/');
447         string_replace(svc2, ' ','_');
448
449         memset(args, 0, sizeof(args[0])*20);
450
451         i=0;
452         args[i++] = "smbmnt";
453
454         args[i++] = mount_point;
455         args[i++] = "-s";
456         args[i++] = svc2;
457
458         if (mount_ro) {
459                 args[i++] = "-r";
460         }
461         if (mount_uid) {
462                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
463                 args[i++] = "-u";
464                 args[i++] = xstrdup(tmp);
465         }
466         if (mount_gid) {
467                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
468                 args[i++] = "-g";
469                 args[i++] = xstrdup(tmp);
470         }
471         if (mount_fmask) {
472                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
473                 args[i++] = "-f";
474                 args[i++] = xstrdup(tmp);
475         }
476         if (mount_dmask) {
477                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
478                 args[i++] = "-d";
479                 args[i++] = xstrdup(tmp);
480         }
481         if (options) {
482                 args[i++] = "-o";
483                 args[i++] = options;
484         }
485
486         if (sys_fork() == 0) {
487                 if (file_exist(BINDIR "/smbmnt", NULL)) {
488                         execv(BINDIR "/smbmnt", args);
489                         fprintf(stderr,"execv of %s failed. Error was %s.", BINDIR "/smbmnt", strerror(errno));
490                 } else {
491                         execvp("smbmnt", args);
492                         fprintf(stderr,"execvp of smbmnt failed. Error was %s.", strerror(errno) );
493                 }
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:
646       username=<arg>                  SMB username
647       password=<arg>                  SMB password
648       credentials=<filename>          file with username/password
649       netbiosname=<arg>               source NetBIOS name
650       uid=<arg>                       mount uid or username
651       gid=<arg>                       mount gid or groupname
652       port=<arg>                      remote SMB port number
653       fmask=<arg>                     file umask
654       dmask=<arg>                     directory umask
655       debug=<arg>                     debug level
656       ip=<arg>                        destination host or IP address
657       workgroup=<arg>                 workgroup on destination
658       sockopt=<arg>                   TCP socket options
659       scope=<arg>                     NetBIOS scope
660       iocharset=<arg>                 Linux charset (iso8859-1, utf8)
661       codepage=<arg>                  server codepage (cp850)
662       ttl=<arg>                       dircache time to live
663       guest                           don't prompt for a password
664       ro                              mount read-only
665       rw                              mount read-write
666
667 This command is designed to be run from within /bin/mount by giving
668 the option '-t smbfs'. For example:
669   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
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         extern pstring global_scope;
689         char *p;
690
691         if (argc < 2 || argv[1][0] == '-') {
692                 usage();
693                 exit(1);
694         }
695         
696         pstrcpy(service, argv[1]);
697         pstrcpy(mpoint, argv[2]);
698
699         /* Convert any '/' characters in the service name to
700            '\' characters */
701         string_replace(service, '/','\\');
702         argc -= 2;
703         argv += 2;
704
705         opt = getopt(argc, argv, "o:");
706         if(opt != 'o') {
707                 return;
708         }
709
710         options[0] = 0;
711         p = options;
712
713         /*
714          * option parsing from nfsmount.c (util-linux-2.9u)
715          */
716         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
717                 DEBUG(3, ("opts: %s\n", opts));
718                 if ((opteq = strchr(opts, '='))) {
719                         val = atoi(opteq + 1);
720                         *opteq = '\0';
721
722                         if (!strcmp(opts, "username") || 
723                             !strcmp(opts, "logon")) {
724                                 char *lp;
725                                 pstrcpy(username,opteq+1);
726                                 if ((lp=strchr(username,'%'))) {
727                                         *lp = 0;
728                                         pstrcpy(password,lp+1);
729                                         got_pass = True;
730                                         memset(strchr(opteq+1,'%')+1,'X',strlen(password));
731                                 }
732                                 if ((lp=strchr(username,'/'))) {
733                                         *lp = 0;
734                                         pstrcpy(workgroup,lp+1);
735                                 }
736                         } else if(!strcmp(opts, "passwd") ||
737                                   !strcmp(opts, "password")) {
738                                 pstrcpy(password,opteq+1);
739                                 got_pass = True;
740                                 memset(opteq+1,'X',strlen(password));
741                         } else if(!strcmp(opts, "credentials")) {
742                                 pstrcpy(credentials,opteq+1);
743                         } else if(!strcmp(opts, "netbiosname")) {
744                                 pstrcpy(my_netbios_name,opteq+1);
745                         } else if(!strcmp(opts, "uid")) {
746                                 mount_uid = nametouid(opteq+1);
747                         } else if(!strcmp(opts, "gid")) {
748                                 mount_gid = nametogid(opteq+1);
749                         } else if(!strcmp(opts, "port")) {
750                                 smb_port = val;
751                         } else if(!strcmp(opts, "fmask")) {
752                                 mount_fmask = strtol(opteq+1, NULL, 8);
753                         } else if(!strcmp(opts, "dmask")) {
754                                 mount_dmask = strtol(opteq+1, NULL, 8);
755                         } else if(!strcmp(opts, "debug")) {
756                                 DEBUGLEVEL = val;
757                         } else if(!strcmp(opts, "ip")) {
758                                 dest_ip = *interpret_addr2(opteq+1);
759                                 if (zero_ip(dest_ip)) {
760                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
761                                         exit(1);
762                                 }
763                                 have_ip = True;
764                         } else if(!strcmp(opts, "workgroup")) {
765                                 pstrcpy(workgroup,opteq+1);
766                         } else if(!strcmp(opts, "sockopt")) {
767                                 pstrcpy(user_socket_options,opteq+1);
768                         } else if(!strcmp(opts, "scope")) {
769                                 pstrcpy(global_scope,opteq+1);
770                         } else {
771                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
772                                 p += strlen(p);
773                         }
774                 } else {
775                         val = 1;
776                         if(!strcmp(opts, "nocaps")) {
777                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
778                                 exit(1);
779                         } else if(!strcmp(opts, "guest")) {
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         static pstring servicesf = CONFIGFILE;
813         char *p;
814
815         DEBUGLEVEL = 1;
816
817         /* here we are interactive, even if run from autofs */
818         setup_logging("mount.smbfs",True);
819
820         TimeInit();
821         charset_initialise();
822         
823         in_client = True;   /* Make sure that we tell lp_load we are */
824
825         if (getenv("USER")) {
826                 pstrcpy(username,getenv("USER"));
827
828                 if ((p=strchr(username,'%'))) {
829                         *p = 0;
830                         pstrcpy(password,p+1);
831                         got_pass = True;
832                         memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
833                 }
834                 strupper(username);
835         }
836
837         if (getenv("PASSWD")) {
838                 pstrcpy(password,getenv("PASSWD"));
839                 got_pass = True;
840         }
841
842         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
843                 get_password_file();
844                 got_pass = True;
845         }
846
847         if (*username == 0 && getenv("LOGNAME")) {
848                 pstrcpy(username,getenv("LOGNAME"));
849         }
850
851         if (!lp_load(servicesf,True,False,False)) {
852                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
853                         servicesf);
854         }
855
856         parse_mount_smb(argc, argv);
857
858         if (*credentials != 0) {
859                 read_credentials_file(credentials);
860         }
861
862         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
863
864         codepage_initialise(lp_client_code_page());
865
866         if (*workgroup == 0) {
867                 pstrcpy(workgroup,lp_workgroup());
868         }
869
870         load_interfaces();
871         if (!*my_netbios_name) {
872                 pstrcpy(my_netbios_name, myhostname());
873         }
874         strupper(my_netbios_name);
875
876         init_mount();
877         return 0;
878 }