2 Unix SMB/Netbios implementation.
5 Copyright (C) Andrew Tridgell 1999
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <asm/types.h>
28 #include <linux/smb_fs.h>
30 extern struct in_addr ipzero;
31 extern int DEBUGLEVEL;
33 extern BOOL in_client;
34 extern pstring user_socket_options;
35 extern BOOL append_log;
36 extern fstring remote_machine;
38 static pstring credentials;
39 static pstring my_netbios_name;
40 static pstring password;
41 static pstring username;
42 static pstring workgroup;
43 static pstring mpoint;
44 static pstring service;
45 static pstring options;
47 static struct in_addr dest_ip;
49 static int smb_port = 139;
51 static uid_t mount_uid;
52 static gid_t mount_gid;
54 static unsigned mount_fmask;
55 static unsigned mount_dmask;
57 static void usage(void);
59 static void exit_parent(int sig)
61 /* parent simply exits when child says go... */
65 static void daemonize(void)
70 signal( SIGTERM, exit_parent );
72 if ((child_pid = sys_fork()) < 0) {
73 DEBUG(0,("could not fork\n"));
78 j = waitpid( child_pid, &status, 0 );
80 if( EINTR == errno ) {
87 /* If we get here - the child exited with some error status */
91 signal( SIGTERM, SIG_DFL );
95 static void close_our_files(int client_fd)
98 for (i = 0; i < 256; i++) {
99 if (i == client_fd) continue;
104 static void usr1_handler(int x)
110 /*****************************************************
111 return a connection to a server
112 *******************************************************/
113 static struct cli_state *do_connection(char *service)
116 struct nmb_name called, calling;
119 extern struct in_addr ipzero;
123 if (service[0] != '\\' || service[1] != '\\') {
128 pstrcpy(server, service+2);
129 share = strchr(server,'\\');
139 make_nmb_name(&calling, my_netbios_name, 0x0);
140 make_nmb_name(&called , server, 0x20);
144 if (have_ip) ip = dest_ip;
146 /* have to open a new connection */
147 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) == 0) ||
148 !cli_connect(c, server_n, &ip)) {
149 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
157 if (!cli_session_request(c, &calling, &called)) {
159 DEBUG(0,("%d: session request to %s failed (%s)\n",
160 getpid(), called.name, cli_errstr(c)));
163 if ((p=strchr(called.name, '.'))) {
167 if (strcmp(called.name, "*SMBSERVER")) {
168 make_nmb_name(&called , "*SMBSERVER", 0x20);
174 DEBUG(4,("%d: session request ok\n", getpid()));
176 if (!cli_negprot(c)) {
177 DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
184 char *pass = getpass("Password: ");
186 pstrcpy(password, pass);
190 if (!cli_session_setup(c, username,
191 password, strlen(password),
192 password, strlen(password),
194 DEBUG(0,("%d: session setup failed: %s\n",
195 getpid(), cli_errstr(c)));
201 DEBUG(4,("%d: session setup ok\n", getpid()));
203 if (!cli_send_tconX(c, share, "?????",
204 password, strlen(password)+1)) {
205 DEBUG(0,("%d: tree connect failed: %s\n",
206 getpid(), cli_errstr(c)));
212 DEBUG(4,("%d: tconx ok\n", getpid()));
220 /****************************************************************************
221 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
222 Code blatently stolen from smbumount.c
224 ****************************************************************************/
225 static void smb_umount(char *mount_point)
233 This routine only gets called to the scene of a disaster
234 to shoot the survivors... A connection that was working
235 has now apparently failed. We have an active mount point
236 (presumably) that we need to dump. If we get errors along
237 the way - make some noise, but we are already turning out
238 the lights to exit anyways...
240 if (umount(mount_point) != 0) {
241 DEBUG(0,("%d: Could not umount %s: %s\n",
242 getpid(), mount_point, strerror(errno)));
246 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
247 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
253 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
254 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
255 getpid(), strerror(errno)));
259 #define MOUNTED_TMP MOUNTED".tmp"
261 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
262 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
263 getpid(), strerror(errno)));
268 while ((mnt = getmntent(mtab)) != NULL) {
269 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
270 addmntent(new_mtab, mnt);
276 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
277 DEBUG(0,("%d: Error changing mode of %s: %s\n",
278 getpid(), MOUNTED_TMP, strerror(errno)));
284 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
285 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
286 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
290 if (unlink(MOUNTED"~") == -1) {
291 DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
298 * Call the smbfs ioctl to install a connection socket,
299 * then wait for a signal to reconnect. Note that we do
300 * not exit after open_sockets() or send_login() errors,
301 * as the smbfs mount would then have no way to recover.
303 static void send_fs_socket(char *service, char *mount_point, struct cli_state *c)
305 int fd, closed = 0, res = 1;
306 pid_t parentpid = getppid();
307 struct smb_conn_opt conn_options;
309 memset(&conn_options, 0, sizeof(conn_options));
312 if ((fd = open(mount_point, O_RDONLY)) < 0) {
313 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
314 getpid(), mount_point));
318 conn_options.fd = c->fd;
319 conn_options.protocol = c->protocol;
320 conn_options.case_handling = SMB_CASE_DEFAULT;
321 conn_options.max_xmit = c->max_xmit;
322 conn_options.server_uid = c->vuid;
323 conn_options.tid = c->cnum;
324 conn_options.secmode = c->sec_mode;
325 conn_options.rawmode = 0;
326 conn_options.sesskey = c->sesskey;
327 conn_options.maxraw = 0;
328 conn_options.capabilities = c->capabilities;
329 conn_options.serverzone = c->serverzone/60;
331 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
333 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
340 /* Ok... We are going to kill the parent. Now
341 is the time to break the process group... */
343 /* Send a signal to the parent to terminate */
344 kill(parentpid, SIGTERM);
350 /* This looks wierd but we are only closing the userspace
351 side, the connection has already been passed to smbfs and
352 it has increased the usage count on the socket.
354 If we don't do this we will "leak" sockets and memory on
355 each reconnection we have to make. */
361 /* redirect stdout & stderr since we can't know that
362 the library functions we use are using DEBUG. */
363 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
364 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
367 dup2(fd, STDOUT_FILENO);
368 dup2(fd, STDERR_FILENO);
372 /* here we are no longer interactive */
373 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
374 setup_logging("mount.smbfs", False);
377 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", service, getpid()));
382 /* Wait for a signal from smbfs ... but don't continue
383 until we actually get a new connection. */
385 CatchSignal(SIGUSR1, &usr1_handler);
387 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
388 c = do_connection(service);
392 smb_umount(mount_point);
393 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
397 /*********************************************************
399 **********************************************************/
400 static char *xstrdup(char *s)
404 fprintf(stderr,"out of memory\n");
411 /****************************************************************************
413 ****************************************************************************/
414 static void init_mount(void)
416 char mount_point[MAXPATHLEN+1];
423 if (realpath(mpoint, mount_point) == NULL) {
424 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
429 c = do_connection(service);
431 fprintf(stderr,"SMB connection failed\n");
436 Set up to return as a daemon child and wait in the parent
437 until the child say it's ready...
441 pstrcpy(svc2, service);
442 string_replace(svc2, '\\','/');
443 string_replace(svc2, ' ','_');
445 memset(args, 0, sizeof(args[0])*20);
448 args[i++] = "smbmnt";
450 args[i++] = mount_point;
458 slprintf(tmp, sizeof(tmp), "%d", mount_uid);
460 args[i++] = xstrdup(tmp);
463 slprintf(tmp, sizeof(tmp), "%d", mount_gid);
465 args[i++] = xstrdup(tmp);
468 slprintf(tmp, sizeof(tmp), "0%o", mount_fmask);
470 args[i++] = xstrdup(tmp);
473 slprintf(tmp, sizeof(tmp), "0%o", mount_dmask);
475 args[i++] = xstrdup(tmp);
482 if (sys_fork() == 0) {
483 if (file_exist(BINDIR "/smbmnt", NULL)) {
484 execv(BINDIR "/smbmnt", args);
485 fprintf(stderr,"execv of %s failed. Error was %s.", BINDIR "/smbmnt", strerror(errno));
487 execvp("smbmnt", args);
488 fprintf(stderr,"execvp of smbmnt failed. Error was %s.", strerror(errno) );
493 if (waitpid(-1, &status, 0) == -1) {
494 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
495 /* FIXME: do some proper error handling */
499 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
500 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
501 /* FIXME: do some proper error handling */
505 /* Ok... This is the rubicon for that mount point... At any point
506 after this, if the connections fail and can not be reconstructed
507 for any reason, we will have to unmount the mount point. There
508 is no exit from the next call...
510 send_fs_socket(service, mount_point, c);
514 /****************************************************************************
515 get a password from a a file or file descriptor
516 exit on failure (from smbclient, move to libsmb or shared .c file?)
517 ****************************************************************************/
518 static void get_password_file(void)
522 BOOL close_it = False;
526 if ((p = getenv("PASSWD_FD")) != NULL) {
527 pstrcpy(spec, "descriptor ");
529 sscanf(p, "%d", &fd);
531 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
532 fd = sys_open(p, O_RDONLY, 0);
535 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
536 spec, strerror(errno));
542 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
543 p && p - pass < sizeof(pass);) {
544 switch (read(fd, p, 1)) {
546 if (*p != '\n' && *p != '\0') {
547 *++p = '\0'; /* advance p, and null-terminate pass */
552 *p = '\0'; /* null-terminate it, just in case... */
553 p = NULL; /* then force the loop condition to become false */
556 fprintf(stderr, "Error reading password from file %s: %s\n",
557 spec, "empty password\n");
562 fprintf(stderr, "Error reading password from file %s: %s\n",
563 spec, strerror(errno));
567 pstrcpy(password, pass);
572 /****************************************************************************
573 get username and password from a credentials file
574 exit on failure (from smbclient, move to libsmb or shared .c file?)
575 ****************************************************************************/
576 static void read_credentials_file(char *filename)
581 char *ptr, *val, *param;
583 if ((auth=sys_fopen(filename, "r")) == NULL)
585 /* fail if we can't open the credentials file */
586 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
592 /* get a line from the file */
593 if (!fgets (buf, sizeof(buf), auth))
597 if ((len) && (buf[len-1]=='\n'))
605 /* break up the line into parameter & value.
606 will need to eat a little whitespace possibly */
608 if (!(ptr = strchr (buf, '=')))
613 /* eat leading white space */
614 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
617 if (strwicmp("password", param) == 0)
619 pstrcpy(password, val);
622 else if (strwicmp("username", param) == 0)
623 pstrcpy(username, val);
625 memset(buf, 0, sizeof(buf));
631 /****************************************************************************
633 ****************************************************************************/
634 static void usage(void)
636 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
638 printf("Version %s\n\n",VERSION);
642 username=<arg> SMB username
643 password=<arg> SMB password
644 netbiosname=<arg> source NetBIOS name
645 uid=<arg> mount uid or username
646 gid=<arg> mount gid or groupname
647 port=<arg> remote SMB port number
648 fmask=<arg> file umask
649 dmask=<arg> directory umask
650 debug=<arg> debug level
651 ip=<arg> destination host or IP address
652 workgroup=<arg> workgroup on destination
653 sockopt=<arg> TCP socket options
654 scope=<arg> NetBIOS scope
655 credentials=<filename> file with username/password
656 guest don't prompt for a password
660 This command is designed to be run from within /bin/mount by giving
661 the option '-t smbfs'. For example:
662 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
667 /****************************************************************************
668 Argument parsing for mount.smbfs interface
669 mount will call us like this:
670 mount.smbfs device mountpoint -o <options>
672 <options> is never empty, containing at least rw or ro
673 ****************************************************************************/
674 static void parse_mount_smb(int argc, char **argv)
681 extern pstring global_scope;
684 if (argc < 2 || argv[1][0] == '-') {
689 pstrcpy(service, argv[1]);
690 pstrcpy(mpoint, argv[2]);
692 /* Convert any '/' characters in the service name to
694 string_replace(service, '/','\\');
698 opt = getopt(argc, argv, "o:");
707 * option parsing from nfsmount.c (util-linux-2.9u)
709 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
710 DEBUG(3, ("opts: %s\n", opts));
711 if ((opteq = strchr(opts, '='))) {
712 val = atoi(opteq + 1);
715 if (!strcmp(opts, "username") ||
716 !strcmp(opts, "logon")) {
718 pstrcpy(username,opteq+1);
719 if ((lp=strchr(username,'%'))) {
721 pstrcpy(password,lp+1);
723 memset(strchr(opteq+1,'%')+1,'X',strlen(password));
725 if ((lp=strchr(username,'/'))) {
727 pstrcpy(workgroup,lp+1);
729 } else if(!strcmp(opts, "passwd") ||
730 !strcmp(opts, "password")) {
731 pstrcpy(password,opteq+1);
733 memset(opteq+1,'X',strlen(password));
734 } else if(!strcmp(opts, "credentials")) {
735 pstrcpy(credentials,opteq+1);
736 } else if(!strcmp(opts, "netbiosname")) {
737 pstrcpy(my_netbios_name,opteq+1);
738 } else if(!strcmp(opts, "uid")) {
739 mount_uid = nametouid(opteq+1);
740 } else if(!strcmp(opts, "gid")) {
741 mount_gid = nametogid(opteq+1);
742 } else if(!strcmp(opts, "port")) {
744 } else if(!strcmp(opts, "fmask")) {
745 mount_fmask = strtol(opteq+1, NULL, 8);
746 } else if(!strcmp(opts, "dmask")) {
747 mount_dmask = strtol(opteq+1, NULL, 8);
748 } else if(!strcmp(opts, "debug")) {
750 } else if(!strcmp(opts, "ip")) {
751 dest_ip = *interpret_addr2(opteq+1);
752 if (zero_ip(dest_ip)) {
753 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
757 } else if(!strcmp(opts, "workgroup")) {
758 pstrcpy(workgroup,opteq+1);
759 } else if(!strcmp(opts, "sockopt")) {
760 pstrcpy(user_socket_options,opteq+1);
761 } else if(!strcmp(opts, "scope")) {
762 pstrcpy(global_scope,opteq+1);
764 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
769 if(!strcmp(opts, "nocaps")) {
770 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
772 } else if(!strcmp(opts, "guest")) {
774 } else if(!strcmp(opts, "rw")) {
776 } else if(!strcmp(opts, "ro")) {
779 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
793 *(p-1) = 0; /* remove trailing , */
794 DEBUG(3,("passthrough options '%s'\n", options));
798 /****************************************************************************
800 ****************************************************************************/
801 int main(int argc,char *argv[])
805 static pstring servicesf = CONFIGFILE;
810 /* here we are interactive, even if run from autofs */
811 setup_logging("mount.smbfs",True);
814 charset_initialise();
816 in_client = True; /* Make sure that we tell lp_load we are */
818 if (getenv("USER")) {
819 pstrcpy(username,getenv("USER"));
821 if ((p=strchr(username,'%'))) {
823 pstrcpy(password,p+1);
825 memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
830 if (getenv("PASSWD")) {
831 pstrcpy(password,getenv("PASSWD"));
835 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
840 if (*username == 0 && getenv("LOGNAME")) {
841 pstrcpy(username,getenv("LOGNAME"));
844 if (!lp_load(servicesf,True,False,False)) {
845 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
849 parse_mount_smb(argc, argv);
851 if (*credentials != 0) {
852 read_credentials_file(credentials);
855 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
857 codepage_initialise(lp_client_code_page());
859 if (*workgroup == 0) {
860 pstrcpy(workgroup,lp_workgroup());
864 if (!*my_netbios_name) {
865 pstrcpy(my_netbios_name, myhostname());
867 strupper(my_netbios_name);