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