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 BOOL in_client;
31 extern pstring user_socket_options;
32 extern BOOL append_log;
33 extern fstring remote_machine;
35 static pstring credentials;
36 static pstring my_netbios_name;
37 static pstring password;
38 static pstring username;
39 static pstring workgroup;
40 static pstring mpoint;
41 static pstring service;
42 static pstring options;
44 static struct in_addr dest_ip;
46 static int smb_port = 0;
48 static uid_t mount_uid;
49 static gid_t mount_gid;
51 static unsigned mount_fmask;
52 static unsigned mount_dmask;
54 static void usage(void);
56 static void exit_parent(int sig)
58 /* parent simply exits when child says go... */
62 static void daemonize(void)
67 signal( SIGTERM, exit_parent );
69 if ((child_pid = sys_fork()) < 0) {
70 DEBUG(0,("could not fork\n"));
75 j = waitpid( child_pid, &status, 0 );
77 if( EINTR == errno ) {
84 /* If we get here - the child exited with some error status */
88 signal( SIGTERM, SIG_DFL );
92 static void close_our_files(int client_fd)
97 getrlimit(RLIMIT_NOFILE,&limits);
98 for (i = 0; i< limits.rlim_max; i++) {
105 static void usr1_handler(int x)
111 /*****************************************************
112 return a connection to a server
113 *******************************************************/
114 static struct cli_state *do_connection(char *the_service)
117 struct nmb_name called, calling;
123 if (the_service[0] != '\\' || the_service[1] != '\\') {
128 pstrcpy(server, the_service+2);
129 share = strchr_m(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) != smb_port) ||
148 !cli_connect(c, server_n, &ip)) {
149 DEBUG(0,("%d: Connection to %s failed\n", getpid(), server_n));
156 if (!cli_session_request(c, &calling, &called)) {
158 DEBUG(0,("%d: session request to %s failed (%s)\n",
159 getpid(), called.name, cli_errstr(c)));
161 if ((p=strchr_m(called.name, '.'))) {
165 if (strcmp(called.name, "*SMBSERVER")) {
166 make_nmb_name(&called , "*SMBSERVER", 0x20);
172 DEBUG(4,("%d: session request ok\n", getpid()));
174 if (!cli_negprot(c)) {
175 DEBUG(0,("%d: protocol negotiation failed\n", getpid()));
181 char *pass = getpass("Password: ");
183 pstrcpy(password, pass);
187 if (!cli_session_setup(c, username,
188 password, strlen(password),
189 password, strlen(password),
191 /* if a password was not supplied then try again with a
193 if (password[0] || !username[0] ||
194 !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
195 DEBUG(0,("%d: session setup failed: %s\n",
196 getpid(), cli_errstr(c)));
200 DEBUG(0,("Anonymous login successful\n"));
203 DEBUG(4,("%d: session setup ok\n", getpid()));
205 if (!cli_send_tconX(c, share, "?????",
206 password, strlen(password)+1)) {
207 DEBUG(0,("%d: tree connect failed: %s\n",
208 getpid(), cli_errstr(c)));
213 DEBUG(4,("%d: tconx ok\n", getpid()));
221 /****************************************************************************
222 unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
223 Code blatently stolen from smbumount.c
225 ****************************************************************************/
226 static void smb_umount(char *mount_point)
234 This routine only gets called to the scene of a disaster
235 to shoot the survivors... A connection that was working
236 has now apparently failed. We have an active mount point
237 (presumably) that we need to dump. If we get errors along
238 the way - make some noise, but we are already turning out
239 the lights to exit anyways...
241 if (umount(mount_point) != 0) {
242 DEBUG(0,("%d: Could not umount %s: %s\n",
243 getpid(), mount_point, strerror(errno)));
247 if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
248 DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", getpid()));
254 if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
255 DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
256 getpid(), strerror(errno)));
260 #define MOUNTED_TMP MOUNTED".tmp"
262 if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
263 DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
264 getpid(), strerror(errno)));
269 while ((mnt = getmntent(mtab)) != NULL) {
270 if (strcmp(mnt->mnt_dir, mount_point) != 0) {
271 addmntent(new_mtab, mnt);
277 if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
278 DEBUG(0,("%d: Error changing mode of %s: %s\n",
279 getpid(), MOUNTED_TMP, strerror(errno)));
285 if (rename(MOUNTED_TMP, MOUNTED) < 0) {
286 DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
287 getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
291 if (unlink(MOUNTED"~") == -1) {
292 DEBUG(0,("%d: Can't remove "MOUNTED"~", getpid()));
299 * Call the smbfs ioctl to install a connection socket,
300 * then wait for a signal to reconnect. Note that we do
301 * not exit after open_sockets() or send_login() errors,
302 * as the smbfs mount would then have no way to recover.
304 static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
306 int fd, closed = 0, res = 1;
307 pid_t parentpid = getppid();
308 struct smb_conn_opt conn_options;
310 memset(&conn_options, 0, sizeof(conn_options));
313 if ((fd = open(mount_point, O_RDONLY)) < 0) {
314 DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
315 getpid(), mount_point));
319 conn_options.fd = c->fd;
320 conn_options.protocol = c->protocol;
321 conn_options.case_handling = SMB_CASE_DEFAULT;
322 conn_options.max_xmit = c->max_xmit;
323 conn_options.server_uid = c->vuid;
324 conn_options.tid = c->cnum;
325 conn_options.secmode = c->sec_mode;
326 conn_options.rawmode = 0;
327 conn_options.sesskey = c->sesskey;
328 conn_options.maxraw = 0;
329 conn_options.capabilities = c->capabilities;
330 conn_options.serverzone = c->serverzone/60;
332 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
334 DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
341 /* Ok... We are going to kill the parent. Now
342 is the time to break the process group... */
344 /* Send a signal to the parent to terminate */
345 kill(parentpid, SIGTERM);
351 /* This looks wierd but we are only closing the userspace
352 side, the connection has already been passed to smbfs and
353 it has increased the usage count on the socket.
355 If we don't do this we will "leak" sockets and memory on
356 each reconnection we have to make. */
360 /* redirect stdout & stderr since we can't know that
361 the library functions we use are using DEBUG. */
362 if ( (fd = open("/dev/null", O_WRONLY)) < 0)
363 DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
366 dup2(fd, STDOUT_FILENO);
367 dup2(fd, STDERR_FILENO);
371 /* here we are no longer interactive */
372 pstrcpy(remote_machine, "smbmount"); /* sneaky ... */
373 setup_logging("mount.smbfs", False);
376 DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, getpid()));
381 /* Wait for a signal from smbfs ... but don't continue
382 until we actually get a new connection. */
384 CatchSignal(SIGUSR1, &usr1_handler);
386 DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", getpid()));
387 c = do_connection(the_service);
391 smb_umount(mount_point);
392 DEBUG(2,("mount.smbfs[%d]: exit\n", getpid()));
400 static void init_mount(void)
402 char mount_point[MAXPATHLEN+1];
409 if (realpath(mpoint, mount_point) == NULL) {
410 fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
415 c = do_connection(service);
417 fprintf(stderr,"SMB connection failed\n");
422 Set up to return as a daemon child and wait in the parent
423 until the child say it's ready...
427 pstrcpy(svc2, service);
428 string_replace(svc2, '\\','/');
429 string_replace(svc2, ' ','_');
431 memset(args, 0, sizeof(args[0])*20);
434 args[i++] = "smbmnt";
436 args[i++] = mount_point;
444 slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
446 args[i++] = smb_xstrdup(tmp);
449 slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
451 args[i++] = smb_xstrdup(tmp);
454 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
456 args[i++] = smb_xstrdup(tmp);
459 slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
461 args[i++] = smb_xstrdup(tmp);
468 if (sys_fork() == 0) {
471 asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
473 if (file_exist(smbmnt_path, NULL)) {
474 execv(smbmnt_path, args);
476 "smbfs/init_mount: execv of %s failed. Error was %s.",
477 smbmnt_path, strerror(errno));
479 execvp("smbmnt", args);
481 "smbfs/init_mount: execv of %s failed. Error was %s.",
482 "smbmnt", strerror(errno));
488 if (waitpid(-1, &status, 0) == -1) {
489 fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
490 /* FIXME: do some proper error handling */
494 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
495 fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
496 /* FIXME: do some proper error handling */
500 /* Ok... This is the rubicon for that mount point... At any point
501 after this, if the connections fail and can not be reconstructed
502 for any reason, we will have to unmount the mount point. There
503 is no exit from the next call...
505 send_fs_socket(service, mount_point, c);
509 /****************************************************************************
510 get a password from a a file or file descriptor
511 exit on failure (from smbclient, move to libsmb or shared .c file?)
512 ****************************************************************************/
513 static void get_password_file(void)
517 BOOL close_it = False;
521 if ((p = getenv("PASSWD_FD")) != NULL) {
522 pstrcpy(spec, "descriptor ");
524 sscanf(p, "%d", &fd);
526 } else if ((p = getenv("PASSWD_FILE")) != NULL) {
527 fd = sys_open(p, O_RDONLY, 0);
530 fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
531 spec, strerror(errno));
537 for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
538 p && p - pass < sizeof(pass);) {
539 switch (read(fd, p, 1)) {
541 if (*p != '\n' && *p != '\0') {
542 *++p = '\0'; /* advance p, and null-terminate pass */
547 *p = '\0'; /* null-terminate it, just in case... */
548 p = NULL; /* then force the loop condition to become false */
551 fprintf(stderr, "Error reading password from file %s: %s\n",
552 spec, "empty password\n");
557 fprintf(stderr, "Error reading password from file %s: %s\n",
558 spec, strerror(errno));
562 pstrcpy(password, pass);
567 /****************************************************************************
568 get username and password from a credentials file
569 exit on failure (from smbclient, move to libsmb or shared .c file?)
570 ****************************************************************************/
571 static void read_credentials_file(char *filename)
576 char *ptr, *val, *param;
578 if ((auth=sys_fopen(filename, "r")) == NULL)
580 /* fail if we can't open the credentials file */
581 DEBUG(0,("ERROR: Unable to open credentials file!\n"));
587 /* get a line from the file */
588 if (!fgets (buf, sizeof(buf), auth))
592 if ((len) && (buf[len-1]=='\n'))
600 /* break up the line into parameter & value.
601 will need to eat a little whitespace possibly */
603 if (!(ptr = strchr (buf, '=')))
608 /* eat leading white space */
609 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
612 if (strwicmp("password", param) == 0)
614 pstrcpy(password, val);
617 else if (strwicmp("username", param) == 0)
618 pstrcpy(username, val);
620 memset(buf, 0, sizeof(buf));
626 /****************************************************************************
628 ****************************************************************************/
629 static void usage(void)
631 printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
633 printf("Version %s\n\n",VERSION);
637 username=<arg> SMB username
638 password=<arg> SMB password
639 credentials=<filename> file with username/password
640 netbiosname=<arg> source NetBIOS name
641 uid=<arg> mount uid or username
642 gid=<arg> mount gid or groupname
643 port=<arg> remote SMB port number
644 fmask=<arg> file umask
645 dmask=<arg> directory umask
646 debug=<arg> debug level
647 ip=<arg> destination host or IP address
648 workgroup=<arg> workgroup on destination
649 sockopt=<arg> TCP socket options
650 scope=<arg> NetBIOS scope
651 iocharset=<arg> Linux charset (iso8859-1, utf8)
652 codepage=<arg> server codepage (cp850)
653 ttl=<arg> dircache time to live
654 guest don't prompt for a password
658 This command is designed to be run from within /bin/mount by giving
659 the option '-t smbfs'. For example:
660 mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test
665 /****************************************************************************
666 Argument parsing for mount.smbfs interface
667 mount will call us like this:
668 mount.smbfs device mountpoint -o <options>
670 <options> is never empty, containing at least rw or ro
671 ****************************************************************************/
672 static void parse_mount_smb(int argc, char **argv)
679 extern pstring global_scope;
682 if (argc < 2 || argv[1][0] == '-') {
687 pstrcpy(service, argv[1]);
688 pstrcpy(mpoint, argv[2]);
690 /* Convert any '/' characters in the service name to
692 string_replace(service, '/','\\');
696 opt = getopt(argc, argv, "o:");
705 * option parsing from nfsmount.c (util-linux-2.9u)
707 for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
708 DEBUG(3, ("opts: %s\n", opts));
709 if ((opteq = strchr_m(opts, '='))) {
710 val = atoi(opteq + 1);
713 if (!strcmp(opts, "username") ||
714 !strcmp(opts, "logon")) {
716 pstrcpy(username,opteq+1);
717 if ((lp=strchr_m(username,'%'))) {
719 pstrcpy(password,lp+1);
721 memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
723 if ((lp=strchr_m(username,'/'))) {
725 pstrcpy(workgroup,lp+1);
727 } else if(!strcmp(opts, "passwd") ||
728 !strcmp(opts, "password")) {
729 pstrcpy(password,opteq+1);
731 memset(opteq+1,'X',strlen(password));
732 } else if(!strcmp(opts, "credentials")) {
733 pstrcpy(credentials,opteq+1);
734 } else if(!strcmp(opts, "netbiosname")) {
735 pstrcpy(my_netbios_name,opteq+1);
736 } else if(!strcmp(opts, "uid")) {
737 mount_uid = nametouid(opteq+1);
738 } else if(!strcmp(opts, "gid")) {
739 mount_gid = nametogid(opteq+1);
740 } else if(!strcmp(opts, "port")) {
742 } else if(!strcmp(opts, "fmask")) {
743 mount_fmask = strtol(opteq+1, NULL, 8);
744 } else if(!strcmp(opts, "dmask")) {
745 mount_dmask = strtol(opteq+1, NULL, 8);
746 } else if(!strcmp(opts, "debug")) {
748 } else if(!strcmp(opts, "ip")) {
749 dest_ip = *interpret_addr2(opteq+1);
750 if (is_zero_ip(dest_ip)) {
751 fprintf(stderr,"Can't resolve address %s\n", opteq+1);
755 } else if(!strcmp(opts, "workgroup")) {
756 pstrcpy(workgroup,opteq+1);
757 } else if(!strcmp(opts, "sockopt")) {
758 pstrcpy(user_socket_options,opteq+1);
759 } else if(!strcmp(opts, "scope")) {
760 pstrcpy(global_scope,opteq+1);
762 slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
767 if(!strcmp(opts, "nocaps")) {
768 fprintf(stderr, "Unhandled option: %s\n", opteq+1);
770 } else if(!strcmp(opts, "guest")) {
773 } else if(!strcmp(opts, "rw")) {
775 } else if(!strcmp(opts, "ro")) {
778 strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
792 *(p-1) = 0; /* remove trailing , */
793 DEBUG(3,("passthrough options '%s'\n", options));
797 /****************************************************************************
799 ****************************************************************************/
800 int main(int argc,char *argv[])
808 /* here we are interactive, even if run from autofs */
809 setup_logging("mount.smbfs",True);
811 /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
812 is to not announce any unicode capabilities as current smbfs does
814 p = getenv("CLI_FORCE_ASCII");
815 if (p && !strcmp(p, "false"))
816 unsetenv("CLI_FORCE_ASCII");
818 setenv("CLI_FORCE_ASCII", "true", 1);
820 in_client = True; /* Make sure that we tell lp_load we are */
822 if (getenv("USER")) {
823 pstrcpy(username,getenv("USER"));
825 if ((p=strchr_m(username,'%'))) {
827 pstrcpy(password,p+1);
829 memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
834 if (getenv("PASSWD")) {
835 pstrcpy(password,getenv("PASSWD"));
839 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
844 if (*username == 0 && getenv("LOGNAME")) {
845 pstrcpy(username,getenv("LOGNAME"));
848 if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
849 fprintf(stderr, "Can't load %s - run testparm to debug it\n",
853 parse_mount_smb(argc, argv);
855 if (*credentials != 0) {
856 read_credentials_file(credentials);
859 DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
861 if (*workgroup == 0) {
862 pstrcpy(workgroup,lp_workgroup());
866 if (!*my_netbios_name) {
867 pstrcpy(my_netbios_name, myhostname());
869 strupper(my_netbios_name);