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