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