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