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 = 0;
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_m(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) != smb_port) ||
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_m(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 /* if a password was not supplied then try again with a
200 if (password[0] || !username[0] ||
201 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
202 DEBUG(0,("%d: session setup failed: %s\n",
203 getpid(), cli_errstr(c)));
208 DEBUG(0,("Anonymous login successful\n"));
211 DEBUG(4,("%d: session setup ok\n", getpid()));
213 if (!cli_send_tconX(c, share, "?????",
214 password, strlen(password)+1)) {
215 DEBUG(0,("%d: tree connect failed: %s\n",
216 getpid(), cli_errstr(c)));
222 DEBUG(4,("%d: tconx ok\n", getpid()));
230 /****************************************************************************
231 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
232 Code blatently stolen from smbumount.c
234 ****************************************************************************/
235 static void smb_umount(char *mount_point)
243 This routine only gets called to the scene of a disaster
244 to shoot the survivors... A connection that was working
245 has now apparently failed. We have an active mount point
246 (presumably) that we need to dump. If we get errors along
247 the way - make some noise, but we are already turning out
248 the lights to exit anyways...
250 if (umount(mount_point) != 0) {
251 DEBUG(0,("%d: Could not umount %s: %s\n",
252 getpid(), mount_point, strerror(errno)));
256 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
257 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
263 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
264 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
265 getpid(), strerror(errno)));
269 #define MOUNTED_TMP MOUNTED".tmp"
271 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
272 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
273 getpid(), strerror(errno)));
278 while ((mnt = getmntent(mtab)) != NULL) {
279 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
280 addmntent(new_mtab, mnt);
286 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
287 DEBUG(0,("%d: Error changing mode of %s: %s\n",
288 getpid(), MOUNTED_TMP, strerror(errno)));
294 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
295 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
296 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
300 if (unlink(MOUNTED"~") == -1) {
301 DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
308 * Call the smbfs ioctl to install a connection socket,
309 * then wait for a signal to reconnect. Note that we do
310 * not exit after open_sockets() or send_login() errors,
311 * as the smbfs mount would then have no way to recover.
313 static void send_fs_socket(char *service, char *mount_point, struct cli_state *c)
315 int fd, closed = 0, res = 1;
316 pid_t parentpid = getppid();
317 struct smb_conn_opt conn_options;
319 memset(&conn_options, 0, sizeof(conn_options));
322 if ((fd = open(mount_point, O_RDONLY)) < 0) {
323 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
324 getpid(), mount_point));
328 conn_options.fd = c->fd;
329 conn_options.protocol = c->protocol;
330 conn_options.case_handling = SMB_CASE_DEFAULT;
331 conn_options.max_xmit = c->max_xmit;
332 conn_options.server_uid = c->vuid;
333 conn_options.tid = c->cnum;
334 conn_options.secmode = c->sec_mode;
335 conn_options.rawmode = 0;
336 conn_options.sesskey = c->sesskey;
337 conn_options.maxraw = 0;
338 conn_options.capabilities = c->capabilities;
339 conn_options.serverzone = c->serverzone/60;
341 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
343 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
350 /* Ok... We are going to kill the parent. Now
351 is the time to break the process group... */
353 /* Send a signal to the parent to terminate */
354 kill(parentpid, SIGTERM);
360 /* This looks wierd but we are only closing the userspace
361 side, the connection has already been passed to smbfs and
362 it has increased the usage count on the socket.
364 If we don't do this we will "leak" sockets and memory on
365 each reconnection we have to make. */
370 /* redirect stdout & stderr since we can't know that
371 the library functions we use are using DEBUG. */
372 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
373 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
376 dup2(fd, STDOUT_FILENO);
377 dup2(fd, STDERR_FILENO);
381 /* here we are no longer interactive */
382 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
383 setup_logging("mount.smbfs", False);
386 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", service, getpid()));
391 /* Wait for a signal from smbfs ... but don't continue
392 until we actually get a new connection. */
394 CatchSignal(SIGUSR1, &usr1_handler);
396 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
397 c = do_connection(service);
401 smb_umount(mount_point);
402 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
406 /*********************************************************
408 **********************************************************/
409 static char *xstrdup(char *s)
413 fprintf(stderr,"out of memory\n");
420 /****************************************************************************
422 ****************************************************************************/
423 static void init_mount(void)
425 char mount_point[MAXPATHLEN+1];
432 if (realpath(mpoint, mount_point) == NULL) {
433 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
438 c = do_connection(service);
440 fprintf(stderr,"SMB connection failed\n");
445 Set up to return as a daemon child and wait in the parent
446 until the child say it's ready...
450 pstrcpy(svc2, service);
451 string_replace(svc2, '\\','/');
452 string_replace(svc2, ' ','_');
454 memset(args, 0, sizeof(args[0])*20);
457 args[i++] = "smbmnt";
459 args[i++] = mount_point;
467 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
469 args[i++] = xstrdup(tmp);
472 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
474 args[i++] = xstrdup(tmp);
477 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
479 args[i++] = xstrdup(tmp);
482 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
484 args[i++] = xstrdup(tmp);
491 if (sys_fork() == 0) {
492 if (file_exist(BINDIR "/smbmnt", NULL)) {
493 execv(BINDIR "/smbmnt", args);
494 fprintf(stderr,"execv of %s failed. Error was %s.", BINDIR "/smbmnt", strerror(errno));
496 execvp("smbmnt", args);
497 fprintf(stderr,"execvp of smbmnt failed. Error was %s.", strerror(errno) );
502 if (waitpid(-1, &status, 0) == -1) {
503 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
504 /* FIXME: do some proper error handling */
508 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
509 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
510 /* FIXME: do some proper error handling */
514 /* Ok... This is the rubicon for that mount point... At any point
515 after this, if the connections fail and can not be reconstructed
516 for any reason, we will have to unmount the mount point. There
517 is no exit from the next call...
519 send_fs_socket(service, mount_point, c);
523 /****************************************************************************
524 get a password from a a file or file descriptor
525 exit on failure (from smbclient, move to libsmb or shared .c file?)
526 ****************************************************************************/
527 static void get_password_file(void)
531 BOOL close_it = False;
535 if ((p = getenv("PASSWD_FD")) != NULL) {
536 pstrcpy(spec, "descriptor ");
538 sscanf(p, "%d", &fd);
540 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
541 fd = sys_open(p, O_RDONLY, 0);
544 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
545 spec, strerror(errno));
551 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
552 p && p - pass < sizeof(pass);) {
553 switch (read(fd, p, 1)) {
555 if (*p != '\n' && *p != '\0') {
556 *++p = '\0'; /* advance p, and null-terminate pass */
561 *p = '\0'; /* null-terminate it, just in case... */
562 p = NULL; /* then force the loop condition to become false */
565 fprintf(stderr, "Error reading password from file %s: %s\n",
566 spec, "empty password\n");
571 fprintf(stderr, "Error reading password from file %s: %s\n",
572 spec, strerror(errno));
576 pstrcpy(password, pass);
581 /****************************************************************************
582 get username and password from a credentials file
583 exit on failure (from smbclient, move to libsmb or shared .c file?)
584 ****************************************************************************/
585 static void read_credentials_file(char *filename)
590 char *ptr, *val, *param;
592 if ((auth=sys_fopen(filename, "r")) == NULL)
594 /* fail if we can't open the credentials file */
595 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
601 /* get a line from the file */
602 if (!fgets (buf, sizeof(buf), auth))
606 if ((len) && (buf[len-1]=='\n'))
614 /* break up the line into parameter & value.
615 will need to eat a little whitespace possibly */
617 if (!(ptr = strchr (buf, '=')))
622 /* eat leading white space */
623 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
626 if (strwicmp("password", param) == 0)
628 pstrcpy(password, val);
631 else if (strwicmp("username", param) == 0)
632 pstrcpy(username, val);
634 memset(buf, 0, sizeof(buf));
640 /****************************************************************************
642 ****************************************************************************/
643 static void usage(void)
645 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
647 printf("Version %s\n\n",VERSION);
651 username=<arg> SMB username
652 password=<arg> SMB password
653 credentials=<filename> file with username/password
654 netbiosname=<arg> source NetBIOS name
655 uid=<arg> mount uid or username
656 gid=<arg> mount gid or groupname
657 port=<arg> remote SMB port number
658 fmask=<arg> file umask
659 dmask=<arg> directory umask
660 debug=<arg> debug level
661 ip=<arg> destination host or IP address
662 workgroup=<arg> workgroup on destination
663 sockopt=<arg> TCP socket options
664 scope=<arg> NetBIOS scope
665 iocharset=<arg> Linux charset (iso8859-1, utf8)
666 codepage=<arg> server codepage (cp850)
667 ttl=<arg> dircache time to live
668 guest don't prompt for a password
672 This command is designed to be run from within /bin/mount by giving
673 the option '-t smbfs'. For example:
674 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
679 /****************************************************************************
680 Argument parsing for mount.smbfs interface
681 mount will call us like this:
682 mount.smbfs device mountpoint -o <options>
684 <options> is never empty, containing at least rw or ro
685 ****************************************************************************/
686 static void parse_mount_smb(int argc, char **argv)
693 extern pstring global_scope;
696 if (argc < 2 || argv[1][0] == '-') {
701 pstrcpy(service, argv[1]);
702 pstrcpy(mpoint, argv[2]);
704 /* Convert any '/' characters in the service name to
706 string_replace(service, '/','\\');
710 opt = getopt(argc, argv, "o:");
719 * option parsing from nfsmount.c (util-linux-2.9u)
721 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
722 DEBUG(3, ("opts: %s\n", opts));
723 if ((opteq = strchr_m(opts, '='))) {
724 val = atoi(opteq + 1);
727 if (!strcmp(opts, "username") ||
728 !strcmp(opts, "logon")) {
730 pstrcpy(username,opteq+1);
731 if ((lp=strchr_m(username,'%'))) {
733 pstrcpy(password,lp+1);
735 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
737 if ((lp=strchr_m(username,'/'))) {
739 pstrcpy(workgroup,lp+1);
741 } else if(!strcmp(opts, "passwd") ||
742 !strcmp(opts, "password")) {
743 pstrcpy(password,opteq+1);
745 memset(opteq+1,'X',strlen(password));
746 } else if(!strcmp(opts, "credentials")) {
747 pstrcpy(credentials,opteq+1);
748 } else if(!strcmp(opts, "netbiosname")) {
749 pstrcpy(my_netbios_name,opteq+1);
750 } else if(!strcmp(opts, "uid")) {
751 mount_uid = nametouid(opteq+1);
752 } else if(!strcmp(opts, "gid")) {
753 mount_gid = nametogid(opteq+1);
754 } else if(!strcmp(opts, "port")) {
756 } else if(!strcmp(opts, "fmask")) {
757 mount_fmask = strtol(opteq+1, NULL, 8);
758 } else if(!strcmp(opts, "dmask")) {
759 mount_dmask = strtol(opteq+1, NULL, 8);
760 } else if(!strcmp(opts, "debug")) {
762 } else if(!strcmp(opts, "ip")) {
763 dest_ip = *interpret_addr2(opteq+1);
764 if (zero_ip(dest_ip)) {
765 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
769 } else if(!strcmp(opts, "workgroup")) {
770 pstrcpy(workgroup,opteq+1);
771 } else if(!strcmp(opts, "sockopt")) {
772 pstrcpy(user_socket_options,opteq+1);
773 } else if(!strcmp(opts, "scope")) {
774 pstrcpy(global_scope,opteq+1);
776 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
781 if(!strcmp(opts, "nocaps")) {
782 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
784 } else if(!strcmp(opts, "guest")) {
787 } else if(!strcmp(opts, "rw")) {
789 } else if(!strcmp(opts, "ro")) {
792 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
806 *(p-1) = 0; /* remove trailing , */
807 DEBUG(3,("passthrough options '%s'\n", options));
811 /****************************************************************************
813 ****************************************************************************/
814 int main(int argc,char *argv[])
818 static pstring servicesf = CONFIGFILE;
823 /* here we are interactive, even if run from autofs */
824 setup_logging("mount.smbfs",True);
826 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
827 is to not announce any unicode capabilities as current smbfs does
829 p = getenv("CLI_FORCE_ASCII");
830 if (p && !strcmp(p, "false"))
831 unsetenv("CLI_FORCE_ASCII");
833 setenv("CLI_FORCE_ASCII", "true", 1);
837 in_client = True; /* Make sure that we tell lp_load we are */
839 if (getenv("USER")) {
840 pstrcpy(username,getenv("USER"));
842 if ((p=strchr_m(username,'%'))) {
844 pstrcpy(password,p+1);
846 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
851 if (getenv("PASSWD")) {
852 pstrcpy(password,getenv("PASSWD"));
856 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
861 if (*username == 0 && getenv("LOGNAME")) {
862 pstrcpy(username,getenv("LOGNAME"));
865 if (!lp_load(servicesf,True,False,False)) {
866 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
870 parse_mount_smb(argc, argv);
872 if (*credentials != 0) {
873 read_credentials_file(credentials);
876 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
878 if (*workgroup == 0) {
879 pstrcpy(workgroup,lp_workgroup());
883 if (!*my_netbios_name) {
884 pstrcpy(my_netbios_name, myhostname());
886 strupper(my_netbios_name);