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