r23567: Fix bug 4721
[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 #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(
673 "Options:\n\
674       username=<arg>                  SMB username\n\
675       password=<arg>                  SMB password\n\
676       credentials=<filename>          file with username/password\n\
677       krb                             use kerberos (active directory)\n\
678       netbiosname=<arg>               source NetBIOS name\n\
679       uid=<arg>                       mount uid or username\n\
680       gid=<arg>                       mount gid or groupname\n\
681       port=<arg>                      remote SMB port number\n\
682       fmask=<arg>                     file umask\n\
683       dmask=<arg>                     directory umask\n\
684       debug=<arg>                     debug level\n\
685       ip=<arg>                        destination host or IP address\n\
686       workgroup=<arg>                 workgroup on destination\n\
687       sockopt=<arg>                   TCP socket options\n\
688       scope=<arg>                     NetBIOS scope\n\
689       iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
690       codepage=<arg>                  server codepage (cp850)\n\
691       unicode                         use unicode when communicating with server\n\
692       lfs                             large file system support\n\
693       ttl=<arg>                       dircache time to live\n\
694       guest                           don't prompt for a password\n\
695       ro                              mount read-only\n\
696       rw                              mount read-write\n\
697 \n\
698 This command is designed to be run from within /bin/mount by giving\n\
699 the option '-t smbfs'. For example:\n\
700   mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
701 ");
702 }
703
704
705 /****************************************************************************
706   Argument parsing for mount.smbfs interface
707   mount will call us like this:
708     mount.smbfs device mountpoint -o <options>
709   
710   <options> is never empty, containing at least rw or ro
711  ****************************************************************************/
712 static void parse_mount_smb(int argc, char **argv)
713 {
714         int opt;
715         char *opts;
716         char *opteq;
717         int val;
718         char *p;
719
720         /* FIXME: This function can silently fail if the arguments are
721          * not in the expected order.
722
723         > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
724         > requires that one gives "-o" before further options like username=...
725         > . Without -o, the username=.. setting is *silently* ignored. I've
726         > spent about an hour trying to find out why I couldn't log in now..
727
728         */
729
730
731         if (argc < 2 || argv[1][0] == '-') {
732                 usage();
733                 exit(1);
734         }
735         
736         pstrcpy(service, argv[1]);
737         pstrcpy(mpoint, argv[2]);
738
739         /* Convert any '/' characters in the service name to
740            '\' characters */
741         string_replace(service, '/','\\');
742         argc -= 2;
743         argv += 2;
744
745         opt = getopt(argc, argv, "o:");
746         if(opt != 'o') {
747                 return;
748         }
749
750         options[0] = 0;
751         p = options;
752
753         /*
754          * option parsing from nfsmount.c (util-linux-2.9u)
755          */
756         for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
757                 DEBUG(3, ("opts: %s\n", opts));
758                 if ((opteq = strchr_m(opts, '='))) {
759                         val = atoi(opteq + 1);
760                         *opteq = '\0';
761
762                         if (!strcmp(opts, "username") || 
763                             !strcmp(opts, "logon")) {
764                                 char *lp;
765                                 got_user = True;
766                                 pstrcpy(username,opteq+1);
767                                 if ((lp=strchr_m(username,'%'))) {
768                                         *lp = 0;
769                                         pstrcpy(password,lp+1);
770                                         got_pass = True;
771                                         memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
772                                 }
773                                 if ((lp=strchr_m(username,'/'))) {
774                                         *lp = 0;
775                                         pstrcpy(workgroup,lp+1);
776                                 }
777                         } else if(!strcmp(opts, "passwd") ||
778                                   !strcmp(opts, "password")) {
779                                 pstrcpy(password,opteq+1);
780                                 got_pass = True;
781                                 memset(opteq+1,'X',strlen(password));
782                         } else if(!strcmp(opts, "credentials")) {
783                                 pstrcpy(credentials,opteq+1);
784                         } else if(!strcmp(opts, "netbiosname")) {
785                                 pstrcpy(my_netbios_name,opteq+1);
786                         } else if(!strcmp(opts, "uid")) {
787                                 mount_uid = nametouid(opteq+1);
788                         } else if(!strcmp(opts, "gid")) {
789                                 mount_gid = nametogid(opteq+1);
790                         } else if(!strcmp(opts, "port")) {
791                                 smb_port = val;
792                         } else if(!strcmp(opts, "fmask")) {
793                                 mount_fmask = strtol(opteq+1, NULL, 8);
794                         } else if(!strcmp(opts, "dmask")) {
795                                 mount_dmask = strtol(opteq+1, NULL, 8);
796                         } else if(!strcmp(opts, "debug")) {
797                                 DEBUGLEVEL = val;
798                         } else if(!strcmp(opts, "ip")) {
799                                 dest_ip = *interpret_addr2(opteq+1);
800                                 if (is_zero_ip(dest_ip)) {
801                                         fprintf(stderr,"Can't resolve address %s\n", opteq+1);
802                                         exit(1);
803                                 }
804                                 have_ip = True;
805                         } else if(!strcmp(opts, "workgroup")) {
806                                 pstrcpy(workgroup,opteq+1);
807                         } else if(!strcmp(opts, "sockopt")) {
808                                 pstrcpy(user_socket_options,opteq+1);
809                         } else if(!strcmp(opts, "scope")) {
810                                 set_global_scope(opteq+1);
811                         } else {
812                                 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
813                                 p += strlen(p);
814                         }
815                 } else {
816                         val = 1;
817                         if(!strcmp(opts, "nocaps")) {
818                                 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
819                                 exit(1);
820                         } else if(!strcmp(opts, "guest")) {
821                                 *password = '\0';
822                                 got_pass = True;
823                         } else if(!strcmp(opts, "krb")) {
824 #ifdef HAVE_KRB5
825
826                                 use_kerberos = True;
827                                 if(!status32_smbfs)
828                                         fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
829 #else
830                                 fprintf(stderr,"No kerberos support compiled in\n");
831                                 exit(1);
832 #endif
833                         } else if(!strcmp(opts, "rw")) {
834                                 mount_ro = 0;
835                         } else if(!strcmp(opts, "ro")) {
836                                 mount_ro = 1;
837                         } else if(!strcmp(opts, "unicode")) {
838                                 smbfs_has_unicode = True;
839                         } else if(!strcmp(opts, "lfs")) {
840                                 smbfs_has_lfs = True;
841                         } else {
842                                 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
843                                 p += strlen(opts);
844                                 *p++ = ',';
845                                 *p = 0;
846                         }
847                 }
848         }
849
850         if (!*service) {
851                 usage();
852                 exit(1);
853         }
854
855         if (p != options) {
856                 *(p-1) = 0;     /* remove trailing , */
857                 DEBUG(3,("passthrough options '%s'\n", options));
858         }
859 }
860
861 /****************************************************************************
862   main program
863 ****************************************************************************/
864  int main(int argc,char *argv[])
865 {
866         char *p;
867
868         DEBUGLEVEL = 1;
869
870         load_case_tables();
871
872         /* here we are interactive, even if run from autofs */
873         setup_logging("mount.smbfs",True);
874
875 #if 0 /* JRA - Urban says not needed ? */
876         /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
877            is to not announce any unicode capabilities as current smbfs does
878            not support it. */
879         p = getenv("CLI_FORCE_ASCII");
880         if (p && !strcmp(p, "false"))
881                 unsetenv("CLI_FORCE_ASCII");
882         else
883                 setenv("CLI_FORCE_ASCII", "true", 1);
884 #endif
885
886         in_client = True;   /* Make sure that we tell lp_load we are */
887
888         if (getenv("USER")) {
889                 pstrcpy(username,getenv("USER"));
890
891                 if ((p=strchr_m(username,'%'))) {
892                         *p = 0;
893                         pstrcpy(password,p+1);
894                         got_pass = True;
895                         memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
896                 }
897                 strupper_m(username);
898         }
899
900         if (getenv("PASSWD")) {
901                 pstrcpy(password,getenv("PASSWD"));
902                 got_pass = True;
903         }
904
905         if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
906                 get_password_file();
907                 got_pass = True;
908         }
909
910         if (*username == 0 && getenv("LOGNAME")) {
911                 pstrcpy(username,getenv("LOGNAME"));
912         }
913
914         if (!lp_load(dyn_CONFIGFILE,True,False,False,True)) {
915                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
916                         dyn_CONFIGFILE);
917         }
918
919         parse_mount_smb(argc, argv);
920
921         if (use_kerberos && !got_user) {
922                 got_pass = True;
923         }
924
925         if (*credentials != 0) {
926                 read_credentials_file(credentials);
927         }
928
929         DEBUG(3,("mount.smbfs started (version %s)\n", SAMBA_VERSION_STRING));
930
931         if (*workgroup == 0) {
932                 pstrcpy(workgroup,lp_workgroup());
933         }
934
935         load_interfaces();
936         if (!*my_netbios_name) {
937                 pstrcpy(my_netbios_name, myhostname());
938         }
939         strupper_m(my_netbios_name);
940
941         init_mount();
942         return 0;
943 }