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