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