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