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)
100 getrlimit(RLIMIT_NOFILE,&limits);
101 for (i = 0; i< limits.rlim_max; i++) {
108 static void usr1_handler(int x)
114 /*****************************************************
115 return a connection to a server
116 *******************************************************/
117 static struct cli_state *do_connection(char *service)
120 struct nmb_name called, calling;
123 extern struct in_addr ipzero;
127 if (service[0] != '\\' || service[1] != '\\') {
132 pstrcpy(server, service+2);
133 share = strchr(server,'\\');
143 make_nmb_name(&calling, my_netbios_name, 0x0);
144 make_nmb_name(&called , server, 0x20);
148 if (have_ip) ip = dest_ip;
150 /* have to open a new connection */
151 if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) == 0) ||
152 !cli_connect(c, server_n, &ip)) {
153 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
161 if (!cli_session_request(c, &calling, &called)) {
163 DEBUG(0,("%d: session request to %s failed (%s)\n",
164 getpid(), called.name, cli_errstr(c)));
167 if ((p=strchr(called.name, '.'))) {
171 if (strcmp(called.name, "*SMBSERVER")) {
172 make_nmb_name(&called , "*SMBSERVER", 0x20);
178 DEBUG(4,("%d: session request ok\n", getpid()));
180 if (!cli_negprot(c)) {
181 DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
188 char *pass = getpass("Password: ");
190 pstrcpy(password, pass);
194 if (!cli_session_setup(c, username,
195 password, strlen(password),
196 password, strlen(password),
198 DEBUG(0,("%d: session setup failed: %s\n",
199 getpid(), cli_errstr(c)));
205 DEBUG(4,("%d: session setup ok\n", getpid()));
207 if (!cli_send_tconX(c, share, "?????",
208 password, strlen(password)+1)) {
209 DEBUG(0,("%d: tree connect failed: %s\n",
210 getpid(), cli_errstr(c)));
216 DEBUG(4,("%d: tconx ok\n", getpid()));
224 /****************************************************************************
225 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
226 Code blatently stolen from smbumount.c
228 ****************************************************************************/
229 static void smb_umount(char *mount_point)
237 This routine only gets called to the scene of a disaster
238 to shoot the survivors... A connection that was working
239 has now apparently failed. We have an active mount point
240 (presumably) that we need to dump. If we get errors along
241 the way - make some noise, but we are already turning out
242 the lights to exit anyways...
244 if (umount(mount_point) != 0) {
245 DEBUG(0,("%d: Could not umount %s: %s\n",
246 getpid(), mount_point, strerror(errno)));
250 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
251 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
257 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
258 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
259 getpid(), strerror(errno)));
263 #define MOUNTED_TMP MOUNTED".tmp"
265 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
266 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
267 getpid(), strerror(errno)));
272 while ((mnt = getmntent(mtab)) != NULL) {
273 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
274 addmntent(new_mtab, mnt);
280 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
281 DEBUG(0,("%d: Error changing mode of %s: %s\n",
282 getpid(), MOUNTED_TMP, strerror(errno)));
288 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
289 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
290 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
294 if (unlink(MOUNTED"~") == -1) {
295 DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
302 * Call the smbfs ioctl to install a connection socket,
303 * then wait for a signal to reconnect. Note that we do
304 * not exit after open_sockets() or send_login() errors,
305 * as the smbfs mount would then have no way to recover.
307 static void send_fs_socket(char *service, char *mount_point, struct cli_state *c)
309 int fd, closed = 0, res = 1;
310 pid_t parentpid = getppid();
311 struct smb_conn_opt conn_options;
313 memset(&conn_options, 0, sizeof(conn_options));
316 if ((fd = open(mount_point, O_RDONLY)) < 0) {
317 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
318 getpid(), mount_point));
322 conn_options.fd = c->fd;
323 conn_options.protocol = c->protocol;
324 conn_options.case_handling = SMB_CASE_DEFAULT;
325 conn_options.max_xmit = c->max_xmit;
326 conn_options.server_uid = c->vuid;
327 conn_options.tid = c->cnum;
328 conn_options.secmode = c->sec_mode;
329 conn_options.rawmode = 0;
330 conn_options.sesskey = c->sesskey;
331 conn_options.maxraw = 0;
332 conn_options.capabilities = c->capabilities;
333 conn_options.serverzone = c->serverzone/60;
335 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
337 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
344 /* Ok... We are going to kill the parent. Now
345 is the time to break the process group... */
347 /* Send a signal to the parent to terminate */
348 kill(parentpid, SIGTERM);
354 /* This looks wierd but we are only closing the userspace
355 side, the connection has already been passed to smbfs and
356 it has increased the usage count on the socket.
358 If we don't do this we will "leak" sockets and memory on
359 each reconnection we have to make. */
365 /* redirect stdout & stderr since we can't know that
366 the library functions we use are using DEBUG. */
367 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
368 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
371 dup2(fd, STDOUT_FILENO);
372 dup2(fd, STDERR_FILENO);
376 /* here we are no longer interactive */
377 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
378 setup_logging("mount.smbfs", False);
381 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", service, getpid()));
386 /* Wait for a signal from smbfs ... but don't continue
387 until we actually get a new connection. */
389 CatchSignal(SIGUSR1, &usr1_handler);
391 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
392 c = do_connection(service);
396 smb_umount(mount_point);
397 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
401 /*********************************************************
403 **********************************************************/
404 static char *xstrdup(char *s)
408 fprintf(stderr,"out of memory\n");
415 /****************************************************************************
417 ****************************************************************************/
418 static void init_mount(void)
420 char mount_point[MAXPATHLEN+1];
427 if (realpath(mpoint, mount_point) == NULL) {
428 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
433 c = do_connection(service);
435 fprintf(stderr,"SMB connection failed\n");
440 Set up to return as a daemon child and wait in the parent
441 until the child say it's ready...
445 pstrcpy(svc2, service);
446 string_replace(svc2, '\\','/');
447 string_replace(svc2, ' ','_');
449 memset(args, 0, sizeof(args[0])*20);
452 args[i++] = "smbmnt";
454 args[i++] = mount_point;
462 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
464 args[i++] = xstrdup(tmp);
467 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
469 args[i++] = xstrdup(tmp);
472 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
474 args[i++] = xstrdup(tmp);
477 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
479 args[i++] = xstrdup(tmp);
486 if (sys_fork() == 0) {
487 if (file_exist(BINDIR "/smbmnt", NULL)) {
488 execv(BINDIR "/smbmnt", args);
489 fprintf(stderr,"execv of %s failed. Error was %s.", BINDIR "/smbmnt", strerror(errno));
491 execvp("smbmnt", args);
492 fprintf(stderr,"execvp of smbmnt failed. Error was %s.", strerror(errno) );
497 if (waitpid(-1, &status, 0) == -1) {
498 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
499 /* FIXME: do some proper error handling */
503 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
504 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
505 /* FIXME: do some proper error handling */
509 /* Ok... This is the rubicon for that mount point... At any point
510 after this, if the connections fail and can not be reconstructed
511 for any reason, we will have to unmount the mount point. There
512 is no exit from the next call...
514 send_fs_socket(service, mount_point, c);
518 /****************************************************************************
519 get a password from a a file or file descriptor
520 exit on failure (from smbclient, move to libsmb or shared .c file?)
521 ****************************************************************************/
522 static void get_password_file(void)
526 BOOL close_it = False;
530 if ((p = getenv("PASSWD_FD")) != NULL) {
531 pstrcpy(spec, "descriptor ");
533 sscanf(p, "%d", &fd);
535 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
536 fd = sys_open(p, O_RDONLY, 0);
539 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
540 spec, strerror(errno));
546 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
547 p && p - pass < sizeof(pass);) {
548 switch (read(fd, p, 1)) {
550 if (*p != '\n' && *p != '\0') {
551 *++p = '\0'; /* advance p, and null-terminate pass */
556 *p = '\0'; /* null-terminate it, just in case... */
557 p = NULL; /* then force the loop condition to become false */
560 fprintf(stderr, "Error reading password from file %s: %s\n",
561 spec, "empty password\n");
566 fprintf(stderr, "Error reading password from file %s: %s\n",
567 spec, strerror(errno));
571 pstrcpy(password, pass);
576 /****************************************************************************
577 get username and password from a credentials file
578 exit on failure (from smbclient, move to libsmb or shared .c file?)
579 ****************************************************************************/
580 static void read_credentials_file(char *filename)
585 char *ptr, *val, *param;
587 if ((auth=sys_fopen(filename, "r")) == NULL)
589 /* fail if we can't open the credentials file */
590 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
596 /* get a line from the file */
597 if (!fgets (buf, sizeof(buf), auth))
601 if ((len) && (buf[len-1]=='\n'))
609 /* break up the line into parameter & value.
610 will need to eat a little whitespace possibly */
612 if (!(ptr = strchr (buf, '=')))
617 /* eat leading white space */
618 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
621 if (strwicmp("password", param) == 0)
623 pstrcpy(password, val);
626 else if (strwicmp("username", param) == 0)
627 pstrcpy(username, val);
629 memset(buf, 0, sizeof(buf));
635 /****************************************************************************
637 ****************************************************************************/
638 static void usage(void)
640 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
642 printf("Version %s\n\n",VERSION);
646 username=<arg> SMB username
647 password=<arg> SMB password
648 credentials=<filename> file with username/password
649 netbiosname=<arg> source NetBIOS name
650 uid=<arg> mount uid or username
651 gid=<arg> mount gid or groupname
652 port=<arg> remote SMB port number
653 fmask=<arg> file umask
654 dmask=<arg> directory umask
655 debug=<arg> debug level
656 ip=<arg> destination host or IP address
657 workgroup=<arg> workgroup on destination
658 sockopt=<arg> TCP socket options
659 scope=<arg> NetBIOS scope
660 iocharset=<arg> Linux charset (iso8859-1, utf8)
661 codepage=<arg> server codepage (cp850)
662 ttl=<arg> dircache time to live
663 guest don't prompt for a password
667 This command is designed to be run from within /bin/mount by giving
668 the option '-t smbfs'. For example:
669 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
674 /****************************************************************************
675 Argument parsing for mount.smbfs interface
676 mount will call us like this:
677 mount.smbfs device mountpoint -o <options>
679 <options> is never empty, containing at least rw or ro
680 ****************************************************************************/
681 static void parse_mount_smb(int argc, char **argv)
688 extern pstring global_scope;
691 if (argc < 2 || argv[1][0] == '-') {
696 pstrcpy(service, argv[1]);
697 pstrcpy(mpoint, argv[2]);
699 /* Convert any '/' characters in the service name to
701 string_replace(service, '/','\\');
705 opt = getopt(argc, argv, "o:");
714 * option parsing from nfsmount.c (util-linux-2.9u)
716 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
717 DEBUG(3, ("opts: %s\n", opts));
718 if ((opteq = strchr(opts, '='))) {
719 val = atoi(opteq + 1);
722 if (!strcmp(opts, "username") ||
723 !strcmp(opts, "logon")) {
725 pstrcpy(username,opteq+1);
726 if ((lp=strchr(username,'%'))) {
728 pstrcpy(password,lp+1);
730 memset(strchr(opteq+1,'%')+1,'X',strlen(password));
732 if ((lp=strchr(username,'/'))) {
734 pstrcpy(workgroup,lp+1);
736 } else if(!strcmp(opts, "passwd") ||
737 !strcmp(opts, "password")) {
738 pstrcpy(password,opteq+1);
740 memset(opteq+1,'X',strlen(password));
741 } else if(!strcmp(opts, "credentials")) {
742 pstrcpy(credentials,opteq+1);
743 } else if(!strcmp(opts, "netbiosname")) {
744 pstrcpy(my_netbios_name,opteq+1);
745 } else if(!strcmp(opts, "uid")) {
746 mount_uid = nametouid(opteq+1);
747 } else if(!strcmp(opts, "gid")) {
748 mount_gid = nametogid(opteq+1);
749 } else if(!strcmp(opts, "port")) {
751 } else if(!strcmp(opts, "fmask")) {
752 mount_fmask = strtol(opteq+1, NULL, 8);
753 } else if(!strcmp(opts, "dmask")) {
754 mount_dmask = strtol(opteq+1, NULL, 8);
755 } else if(!strcmp(opts, "debug")) {
757 } else if(!strcmp(opts, "ip")) {
758 dest_ip = *interpret_addr2(opteq+1);
759 if (zero_ip(dest_ip)) {
760 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
764 } else if(!strcmp(opts, "workgroup")) {
765 pstrcpy(workgroup,opteq+1);
766 } else if(!strcmp(opts, "sockopt")) {
767 pstrcpy(user_socket_options,opteq+1);
768 } else if(!strcmp(opts, "scope")) {
769 pstrcpy(global_scope,opteq+1);
771 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
776 if(!strcmp(opts, "nocaps")) {
777 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
779 } else if(!strcmp(opts, "guest")) {
781 } else if(!strcmp(opts, "rw")) {
783 } else if(!strcmp(opts, "ro")) {
786 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
800 *(p-1) = 0; /* remove trailing , */
801 DEBUG(3,("passthrough options '%s'\n", options));
805 /****************************************************************************
807 ****************************************************************************/
808 int main(int argc,char *argv[])
812 static pstring servicesf = CONFIGFILE;
817 /* here we are interactive, even if run from autofs */
818 setup_logging("mount.smbfs",True);
822 in_client = True; /* Make sure that we tell lp_load we are */
824 if (getenv("USER")) {
825 pstrcpy(username,getenv("USER"));
827 if ((p=strchr(username,'%'))) {
829 pstrcpy(password,p+1);
831 memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
836 if (getenv("PASSWD")) {
837 pstrcpy(password,getenv("PASSWD"));
841 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
846 if (*username == 0 && getenv("LOGNAME")) {
847 pstrcpy(username,getenv("LOGNAME"));
850 if (!lp_load(servicesf,True,False,False)) {
851 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
855 parse_mount_smb(argc, argv);
857 if (*credentials != 0) {
858 read_credentials_file(credentials);
861 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
863 if (*workgroup == 0) {
864 pstrcpy(workgroup,lp_workgroup());
868 if (!*my_netbios_name) {
869 pstrcpy(my_netbios_name, myhostname());
871 strupper(my_netbios_name);