Got "medieval on our ass" about adding the -1 to slprintf.
[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)-1, "%d", mount_uid);
459                 args[i++] = "-u";
460                 args[i++] = xstrdup(tmp);
461         }
462         if (mount_gid) {
463                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
464                 args[i++] = "-g";
465                 args[i++] = xstrdup(tmp);
466         }
467         if (mount_fmask) {
468                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
469                 args[i++] = "-f";
470                 args[i++] = xstrdup(tmp);
471         }
472         if (mount_dmask) {
473                 slprintf(tmp, sizeof(tmp)-1, "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       credentials=<filename>          file with username/password
645       netbiosname=<arg>               source NetBIOS name
646       uid=<arg>                       mount uid or username
647       gid=<arg>                       mount gid or groupname
648       port=<arg>                      remote SMB port number
649       fmask=<arg>                     file umask
650       dmask=<arg>                     directory umask
651       debug=<arg>                     debug level
652       ip=<arg>                        destination host or IP address
653       workgroup=<arg>                 workgroup on destination
654       sockopt=<arg>                   TCP socket options
655       scope=<arg>                     NetBIOS scope
656       iocharset=<arg>                 Linux charset (iso8859-1, utf8)
657       codepage=<arg>                  server codepage (cp850)
658       ttl=<arg>                       dircache time to live
659       guest                           don't prompt for a password
660       ro                              mount read-only
661       rw                              mount read-write
662
663 This command is designed to be run from within /bin/mount by giving
664 the option '-t smbfs'. For example:
665   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
666 ");
667 }
668
669
670 /****************************************************************************
671   Argument parsing for mount.smbfs interface
672   mount will call us like this:
673     mount.smbfs device mountpoint -o <options>
674   
675   <options> is never empty, containing at least rw or ro
676  ****************************************************************************/
677 static void parse_mount_smb(int argc, char **argv)
678 {
679         int opt;
680         char *opts;
681         char *opteq;
682         extern char *optarg;
683         int val;
684         extern pstring global_scope;
685         char *p;
686
687         if (argc < 2 || argv[1][0] == '-') {
688                 usage();
689                 exit(1);
690         }
691         
692         pstrcpy(service, argv[1]);
693         pstrcpy(mpoint, argv[2]);
694
695         /* Convert any '/' characters in the service name to
696            '\' characters */
697         string_replace(service, '/','\\');
698         argc -= 2;
699         argv += 2;
700
701         opt = getopt(argc, argv, "o:");
702         if(opt != 'o') {
703                 return;
704         }
705
706         options[0] = 0;
707         p = options;
708
709         /*
710          * option parsing from nfsmount.c (util-linux-2.9u)
711          */
712         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
713                 DEBUG(3, ("opts: %s\n", opts));
714                 if ((opteq = strchr(opts, '='))) {
715                         val = atoi(opteq + 1);
716                         *opteq = '\0';
717
718                         if (!strcmp(opts, "username") || 
719                             !strcmp(opts, "logon")) {
720                                 char *lp;
721                                 pstrcpy(username,opteq+1);
722                                 if ((lp=strchr(username,'%'))) {
723                                         *lp = 0;
724                                         pstrcpy(password,lp+1);
725                                         got_pass = True;
726                                         memset(strchr(opteq+1,'%')+1,'X',strlen(password));
727                                 }
728                                 if ((lp=strchr(username,'/'))) {
729                                         *lp = 0;
730                                         pstrcpy(workgroup,lp+1);
731                                 }
732                         } else if(!strcmp(opts, "passwd") ||
733                                   !strcmp(opts, "password")) {
734                                 pstrcpy(password,opteq+1);
735                                 got_pass = True;
736                                 memset(opteq+1,'X',strlen(password));
737                         } else if(!strcmp(opts, "credentials")) {
738                                 pstrcpy(credentials,opteq+1);
739                         } else if(!strcmp(opts, "netbiosname")) {
740                                 pstrcpy(my_netbios_name,opteq+1);
741                         } else if(!strcmp(opts, "uid")) {
742                                 mount_uid = nametouid(opteq+1);
743                         } else if(!strcmp(opts, "gid")) {
744                                 mount_gid = nametogid(opteq+1);
745                         } else if(!strcmp(opts, "port")) {
746                                 smb_port = val;
747                         } else if(!strcmp(opts, "fmask")) {
748                                 mount_fmask = strtol(opteq+1, NULL, 8);
749                         } else if(!strcmp(opts, "dmask")) {
750                                 mount_dmask = strtol(opteq+1, NULL, 8);
751                         } else if(!strcmp(opts, "debug")) {
752                                 DEBUGLEVEL = val;
753                         } else if(!strcmp(opts, "ip")) {
754                                 dest_ip = *interpret_addr2(opteq+1);
755                                 if (zero_ip(dest_ip)) {
756                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
757                                         exit(1);
758                                 }
759                                 have_ip = True;
760                         } else if(!strcmp(opts, "workgroup")) {
761                                 pstrcpy(workgroup,opteq+1);
762                         } else if(!strcmp(opts, "sockopt")) {
763                                 pstrcpy(user_socket_options,opteq+1);
764                         } else if(!strcmp(opts, "scope")) {
765                                 pstrcpy(global_scope,opteq+1);
766                         } else {
767                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
768                                 p += strlen(p);
769                         }
770                 } else {
771                         val = 1;
772                         if(!strcmp(opts, "nocaps")) {
773                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
774                                 exit(1);
775                         } else if(!strcmp(opts, "guest")) {
776                                 got_pass = True;
777                         } else if(!strcmp(opts, "rw")) {
778                                 mount_ro = 0;
779                         } else if(!strcmp(opts, "ro")) {
780                                 mount_ro = 1;
781                         } else {
782                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
783                                 p += strlen(opts);
784                                 *p++ = ',';
785                                 *p = 0;
786                         }
787                 }
788         }
789
790         if (!*service) {
791                 usage();
792                 exit(1);
793         }
794
795         if (p != options) {
796                 *(p-1) = 0;     /* remove trailing , */
797                 DEBUG(3,("passthrough options '%s'\n", options));
798         }
799 }
800
801 /****************************************************************************
802   main program
803 ****************************************************************************/
804  int main(int argc,char *argv[])
805 {
806         extern char *optarg;
807         extern int optind;
808         static pstring servicesf = CONFIGFILE;
809         char *p;
810
811         DEBUGLEVEL = 1;
812
813         /* here we are interactive, even if run from autofs */
814         setup_logging("mount.smbfs",True);
815
816         TimeInit();
817         charset_initialise();
818         
819         in_client = True;   /* Make sure that we tell lp_load we are */
820
821         if (getenv("USER")) {
822                 pstrcpy(username,getenv("USER"));
823
824                 if ((p=strchr(username,'%'))) {
825                         *p = 0;
826                         pstrcpy(password,p+1);
827                         got_pass = True;
828                         memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
829                 }
830                 strupper(username);
831         }
832
833         if (getenv("PASSWD")) {
834                 pstrcpy(password,getenv("PASSWD"));
835                 got_pass = True;
836         }
837
838         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
839                 get_password_file();
840                 got_pass = True;
841         }
842
843         if (*username == 0 && getenv("LOGNAME")) {
844                 pstrcpy(username,getenv("LOGNAME"));
845         }
846
847         if (!lp_load(servicesf,True,False,False)) {
848                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
849                         servicesf);
850         }
851
852         parse_mount_smb(argc, argv);
853
854         if (*credentials != 0) {
855                 read_credentials_file(credentials);
856         }
857
858         DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
859
860         codepage_initialise(lp_client_code_page());
861
862         if (*workgroup == 0) {
863                 pstrcpy(workgroup,lp_workgroup());
864         }
865
866         load_interfaces();
867         if (!*my_netbios_name) {
868                 pstrcpy(my_netbios_name, myhostname());
869         }
870         strupper(my_netbios_name);
871
872         init_mount();
873         return 0;
874 }