b1462dbddf18103d836298e13d19c48298f4c050
[ira/wip.git] / source3 / client / smbmount.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMBFS mount program
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 #include <mntent.h>
24 #include <asm/types.h>
25 #include <linux/smb_fs.h>
26
27 extern BOOL in_client;
28 extern pstring user_socket_options;
29 extern char *optarg;
30 extern int optind;
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()) || (cli_set_port(c, smb_port) != smb_port) ||
155             !NT_STATUS_IS_OK(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 (!NT_STATUS_IS_OK(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                     !NT_STATUS_IS_OK(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                 c->smb_rw_error = DO_NOT_DO_TDIS;
387                 cli_shutdown(c);
388                 c = NULL;
389
390                 if (!closed) {
391                         /* close the name cache so that close_our_files() doesn't steal its FD */
392                         namecache_shutdown();
393
394                         /* redirect stdout & stderr since we can't know that
395                            the library functions we use are using DEBUG. */
396                         if ( (fd = open("/dev/null", O_WRONLY)) < 0)
397                                 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
398                         close_our_files(fd);
399                         if (fd >= 0) {
400                                 dup2(fd, STDOUT_FILENO);
401                                 dup2(fd, STDERR_FILENO);
402                                 close(fd);
403                         }
404
405                         /* here we are no longer interactive */
406                         set_remote_machine_name("smbmount", False);     /* sneaky ... */
407                         setup_logging("mount.smbfs", False);
408                         reopen_logs();
409                         DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
410
411                         closed = 1;
412                 }
413
414                 /* Wait for a signal from smbfs ... but don't continue
415                    until we actually get a new connection. */
416                 while (!c) {
417                         CatchSignal(SIGUSR1, &usr1_handler);
418                         pause();
419                         DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
420                         c = do_connection(the_service);
421                 }
422         }
423
424         smb_umount(mount_point);
425         DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
426         exit(1);
427 }
428
429
430 /**
431  * Mount a smbfs
432  **/
433 static void init_mount(void)
434 {
435         char mount_point[PATH_MAX+1];
436         pstring tmp;
437         pstring svc2;
438         struct cli_state *c;
439         char *args[20];
440         int i, status;
441
442         if (realpath(mpoint, mount_point) == NULL) {
443                 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
444                 return;
445         }
446
447
448         c = do_connection(service);
449         if (!c) {
450                 fprintf(stderr,"SMB connection failed\n");
451                 exit(1);
452         }
453
454         /*
455                 Set up to return as a daemon child and wait in the parent
456                 until the child say it's ready...
457         */
458         daemonize();
459
460         pstrcpy(svc2, service);
461         string_replace(svc2, '\\','/');
462         string_replace(svc2, ' ','_');
463
464         memset(args, 0, sizeof(args[0])*20);
465
466         i=0;
467         args[i++] = "smbmnt";
468
469         args[i++] = mount_point;
470         args[i++] = "-s";
471         args[i++] = svc2;
472
473         if (mount_ro) {
474                 args[i++] = "-r";
475         }
476         if (mount_uid) {
477                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
478                 args[i++] = "-u";
479                 args[i++] = smb_xstrdup(tmp);
480         }
481         if (mount_gid) {
482                 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
483                 args[i++] = "-g";
484                 args[i++] = smb_xstrdup(tmp);
485         }
486         if (mount_fmask) {
487                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
488                 args[i++] = "-f";
489                 args[i++] = smb_xstrdup(tmp);
490         }
491         if (mount_dmask) {
492                 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
493                 args[i++] = "-d";
494                 args[i++] = smb_xstrdup(tmp);
495         }
496         if (options) {
497                 args[i++] = "-o";
498                 args[i++] = options;
499         }
500
501         if (sys_fork() == 0) {
502                 char *smbmnt_path;
503
504                 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
505                 
506                 if (file_exist(smbmnt_path, NULL)) {
507                         execv(smbmnt_path, args);
508                         fprintf(stderr,
509                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
510                                 smbmnt_path, strerror(errno));
511                 } else {
512                         execvp("smbmnt", args);
513                         fprintf(stderr,
514                                 "smbfs/init_mount: execv of %s failed. Error was %s.",
515                                 "smbmnt", strerror(errno));
516                 }
517                 free(smbmnt_path);
518                 exit(1);
519         }
520
521         if (waitpid(-1, &status, 0) == -1) {
522                 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
523                 /* FIXME: do some proper error handling */
524                 exit(1);
525         }
526
527         if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
528                 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
529                 /* FIXME: do some proper error handling */
530                 exit(1);
531         } else if (WIFSIGNALED(status)) {
532                 fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
533                 exit(1);
534         }
535
536         /* Ok...  This is the rubicon for that mount point...  At any point
537            after this, if the connections fail and can not be reconstructed
538            for any reason, we will have to unmount the mount point.  There
539            is no exit from the next call...
540         */
541         send_fs_socket(service, mount_point, c);
542 }
543
544
545 /****************************************************************************
546 get a password from a a file or file descriptor
547 exit on failure (from smbclient, move to libsmb or shared .c file?)
548 ****************************************************************************/
549 static void get_password_file(void)
550 {
551         int fd = -1;
552         char *p;
553         BOOL close_it = False;
554         pstring spec;
555         char pass[128];
556
557         if ((p = getenv("PASSWD_FD")) != NULL) {
558                 pstrcpy(spec, "descriptor ");
559                 pstrcat(spec, p);
560                 sscanf(p, "%d", &fd);
561                 close_it = False;
562         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
563                 fd = sys_open(p, O_RDONLY, 0);
564                 pstrcpy(spec, p);
565                 if (fd < 0) {
566                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
567                                 spec, strerror(errno));
568                         exit(1);
569                 }
570                 close_it = True;
571         }
572
573         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
574             p && p - pass < sizeof(pass);) {
575                 switch (read(fd, p, 1)) {
576                 case 1:
577                         if (*p != '\n' && *p != '\0') {
578                                 *++p = '\0'; /* advance p, and null-terminate pass */
579                                 break;
580                         }
581                 case 0:
582                         if (p - pass) {
583                                 *p = '\0'; /* null-terminate it, just in case... */
584                                 p = NULL; /* then force the loop condition to become false */
585                                 break;
586                         } else {
587                                 fprintf(stderr, "Error reading password from file %s: %s\n",
588                                         spec, "empty password\n");
589                                 exit(1);
590                         }
591
592                 default:
593                         fprintf(stderr, "Error reading password from file %s: %s\n",
594                                 spec, strerror(errno));
595                         exit(1);
596                 }
597         }
598         pstrcpy(password, pass);
599         if (close_it)
600                 close(fd);
601 }
602
603 /****************************************************************************
604 get username and password from a credentials file
605 exit on failure (from smbclient, move to libsmb or shared .c file?)
606 ****************************************************************************/
607 static void read_credentials_file(char *filename)
608 {
609         FILE *auth;
610         fstring buf;
611         uint16 len = 0;
612         char *ptr, *val, *param;
613
614         if ((auth=sys_fopen(filename, "r")) == NULL)
615         {
616                 /* fail if we can't open the credentials file */
617                 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
618                 exit (-1);
619         }
620
621         while (!feof(auth))
622         {
623                 /* get a line from the file */
624                 if (!fgets (buf, sizeof(buf), auth))
625                         continue;
626                 len = strlen(buf);
627
628                 if ((len) && (buf[len-1]=='\n'))
629                 {
630                         buf[len-1] = '\0';
631                         len--;
632                 }
633                 if (len == 0)
634                         continue;
635
636                 /* break up the line into parameter & value.
637                    will need to eat a little whitespace possibly */
638                 param = buf;
639                 if (!(ptr = strchr (buf, '=')))
640                         continue;
641                 val = ptr+1;
642                 *ptr = '\0';
643
644                 /* eat leading white space */
645                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
646                         val++;
647
648                 if (strwicmp("password", param) == 0)
649                 {
650                         pstrcpy(password, val);
651                         got_pass = True;
652                 }
653                 else if (strwicmp("username", param) == 0) {
654                         pstrcpy(username, val);
655                 }
656
657                 memset(buf, 0, sizeof(buf));
658         }
659         fclose(auth);
660 }
661
662
663 /****************************************************************************
664 usage on the program
665 ****************************************************************************/
666 static void usage(void)
667 {
668         printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
669
670         printf("Version %s\n\n",SAMBA_VERSION_STRING);
671
672         printf("Please be aware that smbfs is deprecated in favor of "
673                "cifs\n\n");
674
675         printf(
676 "Options:\n\
677       username=<arg>                  SMB username\n\
678       password=<arg>                  SMB password\n\
679       credentials=<filename>          file with username/password\n\
680       krb                             use kerberos (active directory)\n\
681       netbiosname=<arg>               source NetBIOS name\n\
682       uid=<arg>                       mount uid or username\n\
683       gid=<arg>                       mount gid or groupname\n\
684       port=<arg>                      remote SMB port number\n\
685       fmask=<arg>                     file umask\n\
686       dmask=<arg>                     directory umask\n\
687       debug=<arg>                     debug level\n\
688       ip=<arg>                        destination host or IP address\n\
689       workgroup=<arg>                 workgroup on destination\n\
690       sockopt=<arg>                   TCP socket options\n\
691       scope=<arg>                     NetBIOS scope\n\
692       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
693       codepage=<arg>                  server codepage (cp850)\n\
694       unicode                         use unicode when communicating with server\n\
695       lfs                             large file system support\n\
696       ttl=<arg>                       dircache time to live\n\
697       guest                           don't prompt for a password\n\
698       ro                              mount read-only\n\
699       rw                              mount read-write\n\
700 \n\
701 This command is designed to be run from within /bin/mount by giving\n\
702 the option '-t smbfs'. For example:\n\
703   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
704 ");
705 }
706
707
708 /****************************************************************************
709   Argument parsing for mount.smbfs interface
710   mount will call us like this:
711     mount.smbfs device mountpoint -o <options>
712   
713   <options> is never empty, containing at least rw or ro
714  ****************************************************************************/
715 static void parse_mount_smb(int argc, char **argv)
716 {
717         int opt;
718         char *opts;
719         char *opteq;
720         int val;
721         char *p;
722
723         /* FIXME: This function can silently fail if the arguments are
724          * not in the expected order.
725
726         > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
727         > requires that one gives "-o" before further options like username=...
728         > . Without -o, the username=.. setting is *silently* ignored. I've
729         > spent about an hour trying to find out why I couldn't log in now..
730
731         */
732
733
734         if (argc < 2 || argv[1][0] == '-') {
735                 usage();
736                 exit(1);
737         }
738         
739         pstrcpy(service, argv[1]);
740         pstrcpy(mpoint, argv[2]);
741
742         /* Convert any '/' characters in the service name to
743            '\' characters */
744         string_replace(service, '/','\\');
745         argc -= 2;
746         argv += 2;
747
748         opt = getopt(argc, argv, "o:");
749         if(opt != 'o') {
750                 return;
751         }
752
753         options[0] = 0;
754         p = options;
755
756         /*
757          * option parsing from nfsmount.c (util-linux-2.9u)
758          */
759         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
760                 DEBUG(3, ("opts: %s\n", opts));
761                 if ((opteq = strchr_m(opts, '='))) {
762                         val = atoi(opteq + 1);
763                         *opteq = '\0';
764
765                         if (!strcmp(opts, "username") || 
766                             !strcmp(opts, "logon")) {
767                                 char *lp;
768                                 got_user = True;
769                                 pstrcpy(username,opteq+1);
770                                 if ((lp=strchr_m(username,'%'))) {
771                                         *lp = 0;
772                                         pstrcpy(password,lp+1);
773                                         got_pass = True;
774                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
775                                 }
776                                 if ((lp=strchr_m(username,'/'))) {
777                                         *lp = 0;
778                                         pstrcpy(workgroup,lp+1);
779                                 }
780                         } else if(!strcmp(opts, "passwd") ||
781                                   !strcmp(opts, "password")) {
782                                 pstrcpy(password,opteq+1);
783                                 got_pass = True;
784                                 memset(opteq+1,'X',strlen(password));
785                         } else if(!strcmp(opts, "credentials")) {
786                                 pstrcpy(credentials,opteq+1);
787                         } else if(!strcmp(opts, "netbiosname")) {
788                                 pstrcpy(my_netbios_name,opteq+1);
789                         } else if(!strcmp(opts, "uid")) {
790                                 mount_uid = nametouid(opteq+1);
791                         } else if(!strcmp(opts, "gid")) {
792                                 mount_gid = nametogid(opteq+1);
793                         } else if(!strcmp(opts, "port")) {
794                                 smb_port = val;
795                         } else if(!strcmp(opts, "fmask")) {
796                                 mount_fmask = strtol(opteq+1, NULL, 8);
797                         } else if(!strcmp(opts, "dmask")) {
798                                 mount_dmask = strtol(opteq+1, NULL, 8);
799                         } else if(!strcmp(opts, "debug")) {
800                                 DEBUGLEVEL = val;
801                         } else if(!strcmp(opts, "ip")) {
802                                 dest_ip = *interpret_addr2(opteq+1);
803                                 if (is_zero_ip(dest_ip)) {
804                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
805                                         exit(1);
806                                 }
807                                 have_ip = True;
808                         } else if(!strcmp(opts, "workgroup")) {
809                                 pstrcpy(workgroup,opteq+1);
810                         } else if(!strcmp(opts, "sockopt")) {
811                                 pstrcpy(user_socket_options,opteq+1);
812                         } else if(!strcmp(opts, "scope")) {
813                                 set_global_scope(opteq+1);
814                         } else {
815                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
816                                 p += strlen(p);
817                         }
818                 } else {
819                         val = 1;
820                         if(!strcmp(opts, "nocaps")) {
821                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
822                                 exit(1);
823                         } else if(!strcmp(opts, "guest")) {
824                                 *password = '\0';
825                                 got_pass = True;
826                         } else if(!strcmp(opts, "krb")) {
827 #ifdef HAVE_KRB5
828
829                                 use_kerberos = True;
830                                 if(!status32_smbfs)
831                                         fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
832 #else
833                                 fprintf(stderr,"No kerberos support compiled in\n");
834                                 exit(1);
835 #endif
836                         } else if(!strcmp(opts, "rw")) {
837                                 mount_ro = 0;
838                         } else if(!strcmp(opts, "ro")) {
839                                 mount_ro = 1;
840                         } else if(!strcmp(opts, "unicode")) {
841                                 smbfs_has_unicode = True;
842                         } else if(!strcmp(opts, "lfs")) {
843                                 smbfs_has_lfs = True;
844                         } else {
845                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
846                                 p += strlen(opts);
847                                 *p++ = ',';
848                                 *p = 0;
849                         }
850                 }
851         }
852
853         if (!*service) {
854                 usage();
855                 exit(1);
856         }
857
858         if (p != options) {
859                 *(p-1) = 0;     /* remove trailing , */
860                 DEBUG(3,("passthrough options '%s'\n", options));
861         }
862 }
863
864 /****************************************************************************
865   main program
866 ****************************************************************************/
867  int main(int argc,char *argv[])
868 {
869         char *p;
870
871         DEBUGLEVEL = 1;
872
873         load_case_tables();
874
875         /* here we are interactive, even if run from autofs */
876         setup_logging("mount.smbfs",True);
877
878 #if 0 /* JRA - Urban says not needed ? */
879         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
880            is to not announce any unicode capabilities as current smbfs does
881            not support it. */
882         p = getenv("CLI_FORCE_ASCII");
883         if (p && !strcmp(p, "false"))
884                 unsetenv("CLI_FORCE_ASCII");
885         else
886                 setenv("CLI_FORCE_ASCII", "true", 1);
887 #endif
888
889         in_client = True;   /* Make sure that we tell lp_load we are */
890
891         if (getenv("USER")) {
892                 pstrcpy(username,getenv("USER"));
893
894                 if ((p=strchr_m(username,'%'))) {
895                         *p = 0;
896                         pstrcpy(password,p+1);
897                         got_pass = True;
898                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
899                 }
900                 strupper_m(username);
901         }
902
903         if (getenv("PASSWD")) {
904                 pstrcpy(password,getenv("PASSWD"));
905                 got_pass = True;
906         }
907
908         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
909                 get_password_file();
910                 got_pass = True;
911         }
912
913         if (*username == 0 && getenv("LOGNAME")) {
914                 pstrcpy(username,getenv("LOGNAME"));
915         }
916
917         if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
918                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
919                         dyn_CONFIGFILE);
920         }
921
922         parse_mount_smb(argc, argv);
923
924         if (use_kerberos && !got_user) {
925                 got_pass = True;
926         }
927
928         if (*credentials != 0) {
929                 read_credentials_file(credentials);
930         }
931
932         DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING));
933
934         if (*workgroup == 0) {
935                 pstrcpy(workgroup,lp_workgroup());
936         }
937
938         load_interfaces();
939         if (!*my_netbios_name) {
940                 pstrcpy(my_netbios_name, myhostname());
941         }
942         strupper_m(my_netbios_name);
943
944         init_mount();
945         return 0;
946 }