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