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