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