364c28cdcdaa6951788240752a783e282117ad55
[samba.git] / source3 / client / smbmount.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    SMB client
5    Copyright (C) Andrew Tridgell 1994-1998
6    
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.
11    
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.
16    
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.
20 */
21
22 #define NO_SYSLOG
23
24 #include <linux/version.h>
25 #define LVERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
26 #if LINUX_VERSION_CODE < LVERSION(2,1,70)
27 #error this code will only compile on versions of linux after 2.1.70
28 #endif
29
30 #include "includes.h"
31
32 #include <asm/types.h>
33 #include <linux/smb_fs.h>
34 static struct smb_conn_opt conn_options;
35
36 #ifndef REGISTER
37 #define REGISTER 0
38 #endif
39
40 /* Uncomment this to allow debug the smbmount daemon */
41 #define SMBFS_DEBUG 1
42
43 pstring cur_dir = "\\";
44 pstring cd_path = "";
45 extern pstring service;
46 extern pstring desthost;
47 extern pstring global_myname;
48 extern pstring myhostname;
49 extern pstring password;
50 extern pstring username;
51 extern pstring workgroup;
52 char *cmdstr="";
53 extern BOOL got_pass;
54 extern BOOL connect_as_printer;
55 extern BOOL connect_as_ipc;
56 extern struct in_addr ipzero;
57
58 extern BOOL doencrypt;
59
60 extern pstring user_socket_options;
61
62 /* 30 second timeout on most commands */
63 #define CLIENT_TIMEOUT (30*1000)
64 #define SHORT_TIMEOUT (5*1000)
65
66 /* value for unused fid field in trans2 secondary request */
67 #define FID_UNUSED (0xFFFF)
68
69 extern int name_type;
70
71 extern int max_protocol;
72 int port = SMB_PORT;
73
74
75 time_t newer_than = 0;
76 int archive_level = 0;
77
78 extern pstring debugf;
79 extern int DEBUGLEVEL;
80
81 BOOL translation = False;
82
83 extern uint16 cnum;
84 extern uint16 mid;
85 extern uint16 pid;
86 extern uint16 vuid;
87
88 extern BOOL have_ip;
89 extern int max_xmit;
90
91 /* clitar bits insert */
92 extern int blocksize;
93 extern BOOL tar_inc;
94 extern BOOL tar_reset;
95 /* clitar bits end */
96  
97
98 mode_t myumask = 0755;
99
100 extern pstring scope;
101
102 BOOL prompt = True;
103
104 int printmode = 1;
105
106 BOOL recurse = False;
107 BOOL lowercase = False;
108
109 struct in_addr dest_ip;
110
111 #define SEPARATORS " \t\n\r"
112
113 BOOL abort_mget = True;
114
115 extern int Protocol;
116
117 extern BOOL readbraw_supported ;
118 extern BOOL writebraw_supported;
119
120 pstring fileselection = "";
121
122 extern file_info def_finfo;
123
124 /* timing globals */
125 int get_total_size = 0;
126 int get_total_time_ms = 0;
127 int put_total_size = 0;
128 int put_total_time_ms = 0;
129
130 /* totals globals */
131 int dir_total = 0;
132
133 extern int Client;
134
135 #define USENMB
136
137 #define CNV_LANG(s) dos_to_unix(s,False)
138 #define CNV_INPUT(s) unix_to_dos(s,True)
139
140 /****************************************************************************
141 check for existance of a dir
142 ****************************************************************************/
143 static BOOL chkpath(char *path,BOOL report)
144 {
145   fstring path2;
146   pstring inbuf,outbuf;
147   char *p;
148
149   fstrcpy(path2,path);
150   trim_string(path2,NULL,"\\");
151   if (!*path2) *path2 = '\\';
152
153   bzero(outbuf,smb_size);
154   set_message(outbuf,0,4 + strlen(path2),True);
155   SCVAL(outbuf,smb_com,SMBchkpth);
156   SSVAL(outbuf,smb_tid,cnum);
157   cli_setup_pkt(outbuf);
158
159   p = smb_buf(outbuf);
160   *p++ = 4;
161   fstrcpy(p,path2);
162
163 #if 0
164   {
165           /* this little bit of code can be used to extract NT error codes.
166              Just feed a bunch of "cd foo" commands to smbclient then watch
167              in netmon (tridge) */
168           static int code=0;
169           SIVAL(outbuf, smb_rcls, code | 0xC0000000);
170           SSVAL(outbuf, smb_flg2, SVAL(outbuf, smb_flg2) | (1<<14));
171           code++;
172   }
173 #endif
174
175   send_smb(Client,outbuf);
176   client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
177
178   if (report && CVAL(inbuf,smb_rcls) != 0)
179     DEBUG(2,("chkpath: %s\n",smb_errstr(inbuf)));
180
181   return(CVAL(inbuf,smb_rcls) == 0);
182 }
183
184 static void
185 daemonize(void)
186 {
187         int i;
188         if ((i = fork()) < 0)
189         {
190                 DEBUG(0, ("could not fork\n"));
191         }
192         if (i > 0)
193         {
194                 /* parent simply exits */
195                 exit(0);
196         }
197         setsid();
198         chdir("/");
199 }
200
201 static void
202 close_our_files(void)
203 {
204         int i;
205         for (i = 0; i < NR_OPEN; i++) {
206                 if (i == Client) {
207                         continue;
208                 }
209                 close(i);
210         }
211 }
212
213 static void
214 usr1_handler(int x)
215 {
216         return;
217 }
218
219 /*
220  * Send a login and store the connection options. This is a separate
221  * function to keep clientutil.c independent of linux kernel changes.
222  */
223 static BOOL mount_send_login(char *inbuf, char *outbuf)
224 {
225   struct connection_options opt;
226   int res = cli_send_login(inbuf, outbuf, True, True, &opt);
227
228   if (!res)
229     return res;
230
231   conn_options.protocol = opt.protocol;
232   conn_options.case_handling = CASE_LOWER;
233   conn_options.max_xmit = opt.max_xmit;
234   conn_options.server_uid = opt.server_vuid;
235   conn_options.tid = opt.tid;
236   conn_options.secmode = opt.sec_mode;
237   conn_options.maxmux = opt.max_mux;
238   conn_options.maxvcs = opt.max_vcs;
239   conn_options.rawmode = opt.rawmode;
240   conn_options.sesskey = opt.sesskey;
241   conn_options.maxraw = opt.maxraw;
242   conn_options.capabilities = opt.capabilities;
243   conn_options.serverzone = opt.serverzone;
244
245   return True;
246 }
247
248 /*
249  * Call the smbfs ioctl to install a connection socket,
250  * then wait for a signal to reconnect. Note that we do
251  * not exit after open_sockets() or send_login() errors,
252  * as the smbfs mount would then have no way to recover.
253  */
254 static void
255 send_fs_socket(char *mount_point, char *inbuf, char *outbuf)
256 {
257         int fd, closed = 0, res = 1;
258
259         while (1)
260         {
261                 if ((fd = open(mount_point, O_RDONLY)) < 0)
262                 {
263                         DEBUG(0, ("smbmount: can't open %s\n", mount_point));
264                         break;
265                 }               
266
267                 /*
268                  * Call the ioctl even if we couldn't get a socket ...
269                  * there's no point in making smbfs wait for a timeout.
270                  */
271                 conn_options.fd = -1;
272                 if (res)
273                         conn_options.fd = Client;
274                 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
275                 if (res != 0)
276                 {
277                         DEBUG(0, ("smbmount: ioctl failed, res=%d\n", res));
278                 }
279
280                 close_sockets();
281                 close(fd);
282                 /*
283                  * Close all open files if we haven't done so yet.
284                  */
285 #ifndef SMBFS_DEBUG
286                 if (!closed)
287                 {
288                         closed = 1;
289                         close_our_files();
290                 }
291 #endif
292
293                 /*
294                  * Wait for a signal from smbfs ...
295                  */
296                 CatchSignal(SIGUSR1, &usr1_handler);
297                 pause();
298                 DEBUG(0, ("smbmount: got signal, getting new socket\n"));
299
300                 res = cli_open_sockets(port);
301                 if (!res)
302                 {
303                         DEBUG(0, ("smbmount: can't open sockets\n"));
304                         continue;
305                 }
306
307                 res = mount_send_login(inbuf, outbuf);
308                 if (!res)
309                 {
310                         DEBUG(0, ("smbmount: login failed\n"));
311                 }
312         }
313         DEBUG(0, ("smbmount: exit\n"));
314         exit(1);
315 }
316
317 /****************************************************************************
318 mount smbfs
319 ****************************************************************************/
320 static void cmd_mount(char *inbuf,char *outbuf)
321 {
322         pstring mpoint;
323         pstring share_name;
324         pstring mount_command;
325         fstring buf;
326         int retval;
327         char mount_point[MAXPATHLEN+1];
328
329         if (!next_token(NULL, mpoint, NULL, sizeof(mpoint)))
330         {
331                 DEBUG(0,("You must supply a mount point\n"));
332                 return;
333         }
334
335         memset(mount_point, 0, sizeof(mount_point));
336
337         if (realpath(mpoint, mount_point) == NULL)
338         {
339                 DEBUG(0, ("Could not resolve mount point\n"));
340                 return;
341         }
342
343         /*
344          * Build the service name to report on the Unix side,
345          * converting '\' to '/' and ' ' to '_'.
346          */
347         pstrcpy(share_name, service);  
348         string_replace(share_name, '\\', '/');
349         string_replace(share_name, ' ', '_');
350
351         slprintf(mount_command, sizeof(mount_command)-1,"smbmnt %s -s %s", mount_point, share_name);
352
353         while(next_token(NULL, buf, NULL, sizeof(buf)))
354         {
355                 pstrcat(mount_command, " ");
356                 pstrcat(mount_command, buf);
357         }
358
359         DEBUG(3, ("mount command: %s\n", mount_command));
360
361         if ((retval = system(mount_command)) != 0)
362         {
363                 DEBUG(0,("mount failed\n"));
364                 exit(1);
365         }
366
367         /*
368          * Create the background process after trying the mount.
369          * to avoid race conditions with automount and other processes.
370          */
371         daemonize();
372
373         /* The parent has exited here, leave the daemon to deal with
374          * disconnects and reconnects
375          */
376         send_fs_socket(mount_point, inbuf, outbuf);
377 }       
378
379
380 /* This defines the commands supported by this client */
381 struct
382 {
383   char *name;
384   void (*fn)();
385   char *description;
386 } commands[] = 
387 {
388   {"mount", cmd_mount, "<mount-point options> mount an smbfs file system"},
389   {"",NULL,NULL}
390 };
391
392
393 /*******************************************************************
394   lookup a command string in the list of commands, including 
395   abbreviations
396   ******************************************************************/
397 static int process_tok(fstring tok)
398 {
399   int i = 0, matches = 0;
400   int cmd=0;
401   int tok_len = strlen(tok);
402   
403   while (commands[i].fn != NULL)
404     {
405       if (strequal(commands[i].name,tok))
406         {
407           matches = 1;
408           cmd = i;
409           break;
410         }
411       else if (strnequal(commands[i].name, tok, tok_len))
412         {
413           matches++;
414           cmd = i;
415         }
416       i++;
417     }
418   
419   if (matches == 0)
420     return(-1);
421   else if (matches == 1)
422     return(cmd);
423   else
424     return(-2);
425 }
426
427 /****************************************************************************
428 help
429 ****************************************************************************/
430 void cmd_help(char *dum_in, char *dum_out)
431 {
432   int i=0,j;
433   fstring buf;
434
435   if (next_token(NULL,buf,NULL,sizeof(buf)))
436     {
437       if ((i = process_tok(buf)) >= 0)
438         DEBUG(0,("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description));                   
439     }
440   else
441     while (commands[i].description)
442       {
443         for (j=0; commands[i].description && (j<5); j++) {
444           DEBUG(0,("%-15s",commands[i].name));
445           i++;
446         }
447         DEBUG(0,("\n"));
448       }
449 }
450
451 /****************************************************************************
452 wait for keyboard activity, swallowing network packets
453 ****************************************************************************/
454 static void wait_keyboard(char *buffer)
455 {
456   fd_set fds;
457   int selrtn;
458   struct timeval timeout;
459   
460   while (1) 
461     {
462       extern int Client;
463       FD_ZERO(&fds);
464       FD_SET(Client,&fds);
465       FD_SET(fileno(stdin),&fds);
466
467       timeout.tv_sec = 20;
468       timeout.tv_usec = 0;
469       selrtn = sys_select(MAX(Client,fileno(stdin))+1,&fds,&timeout);
470       
471       if (FD_ISSET(fileno(stdin),&fds))
472         return;
473
474       /* We deliberately use receive_smb instead of
475          client_receive_smb as we want to receive
476          session keepalives and then drop them here.
477        */
478       if (FD_ISSET(Client,&fds))
479         receive_smb(Client,buffer,0);
480       
481       chkpath("\\",False);
482     }  
483 }
484
485
486 /****************************************************************************
487   process commands from the client
488 ****************************************************************************/
489 static BOOL process(char *base_directory)
490 {
491   extern FILE *dbf;
492   pstring line;
493   char *cmd;
494
495   char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
496   char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
497
498   if ((InBuffer == NULL) || (OutBuffer == NULL)) 
499     return(False);
500   
501   bzero(OutBuffer,smb_size);
502
503   if (!mount_send_login(InBuffer,OutBuffer))
504     return(False);
505
506   cmd = cmdstr;
507   if (cmd[0] != '\0') while (cmd[0] != '\0')
508     {
509       char *p;
510       fstring tok;
511       int i;
512
513       if ((p = strchr(cmd, ';')) == 0)
514         {
515           strncpy(line, cmd, 999);
516           line[1000] = '\0';
517           cmd += strlen(cmd);
518         }
519       else
520         {
521           if (p - cmd > 999) p = cmd + 999;
522           strncpy(line, cmd, p - cmd);
523           line[p - cmd] = '\0';
524           cmd = p + 1;
525         }
526
527       /* input language code to internal one */
528       CNV_INPUT (line);
529       
530       /* and get the first part of the command */
531       {
532         char *ptr = line;
533         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
534       }
535
536       if ((i = process_tok(tok)) >= 0)
537         commands[i].fn(InBuffer,OutBuffer);
538       else if (i == -2)
539         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
540       else
541         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
542     }
543   else while (!feof(stdin))
544     {
545       fstring tok;
546       int i;
547
548       bzero(OutBuffer,smb_size);
549
550       /* display a prompt */
551       DEBUG(0,("smb: %s> ", CNV_LANG(cur_dir)));
552       dbgflush();
553
554       wait_keyboard(InBuffer);
555   
556       /* and get a response */
557       if (!fgets(line,1000,stdin))
558         break;
559
560       /* input language code to internal one */
561       CNV_INPUT (line);
562
563       /* special case - first char is ! */
564       if (*line == '!')
565         {
566           system(line + 1);
567           continue;
568         }
569       
570       /* and get the first part of the command */
571       {
572         char *ptr = line;
573         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
574       }
575
576       if ((i = process_tok(tok)) >= 0)
577         commands[i].fn(InBuffer,OutBuffer);
578       else if (i == -2)
579         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
580       else
581         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
582     }
583   
584   cli_send_logout(InBuffer,OutBuffer);
585   return(True);
586 }
587
588 /****************************************************************************
589 usage on the program
590 ****************************************************************************/
591 static void usage(char *pname)
592 {
593   DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ",
594            pname));
595
596   DEBUG(0,("\nVersion %s\n",VERSION));
597   DEBUG(0,("\t-p port               connect to the specified port\n"));
598   DEBUG(0,("\t-d debuglevel         set the debuglevel\n"));
599   DEBUG(0,("\t-l log basename.      Basename for log/debug files\n"));
600   DEBUG(0,("\t-n netbios name.      Use this name as my netbios name\n"));
601   DEBUG(0,("\t-N                    don't ask for a password\n"));
602   DEBUG(0,("\t-m max protocol       set the max protocol level\n"));
603   DEBUG(0,("\t-I dest IP            use this IP to connect to\n"));
604   DEBUG(0,("\t-E                    write messages to stderr instead of stdout\n"));
605   DEBUG(0,("\t-U username           set the network username\n"));
606   DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
607   DEBUG(0,("\t-c command string     execute semicolon separated commands\n"));
608   DEBUG(0,("\t-t terminal code      terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n"));
609   DEBUG(0,("\t-D directory          start from directory\n"));
610   DEBUG(0,("\n"));
611 }
612
613 /****************************************************************************
614   main program
615 ****************************************************************************/
616  int main(int argc,char *argv[])
617 {
618   fstring base_directory;
619   char *pname = argv[0];
620   int opt;
621   extern FILE *dbf;
622   extern char *optarg;
623   extern int optind;
624   pstring query_host;
625   BOOL nt_domain_logon = False;
626   static pstring servicesf = CONFIGFILE;
627   pstring term_code;
628   char *p;
629
630 #ifdef KANJI
631   pstrcpy(term_code, KANJI);
632 #else /* KANJI */
633   *term_code = 0;
634 #endif /* KANJI */
635
636   *query_host = 0;
637   *base_directory = 0;
638
639   DEBUGLEVEL = 2;
640
641   setup_logging(pname,True);
642
643   TimeInit();
644   charset_initialise();
645
646   pid = (uint16)getpid();
647   vuid = (uint16)getuid();
648   mid = pid + 100;
649   myumask = umask(0);
650   umask(myumask);
651
652   if (getenv("USER"))
653   {
654     pstrcpy(username,getenv("USER"));
655
656     /* modification to support userid%passwd syntax in the USER var
657        25.Aug.97, jdblair@uab.edu */
658
659     if ((p=strchr(username,'%')))
660     {
661       *p = 0;
662       pstrcpy(password,p+1);
663       got_pass = True;
664       memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
665     }
666     strupper(username);
667   }
668
669  /* modification to support PASSWD environmental var
670   25.Aug.97, jdblair@uab.edu */
671
672   if (getenv("PASSWD"))
673     pstrcpy(password,getenv("PASSWD"));
674
675   if (*username == 0 && getenv("LOGNAME"))
676     {
677       pstrcpy(username,getenv("LOGNAME"));
678       strupper(username);
679     }
680
681   if (argc < 2)
682     {
683       usage(pname);
684       exit(1);
685     }
686   
687   if (*argv[1] != '-')
688     {
689
690       pstrcpy(service, argv[1]);  
691       /* Convert any '/' characters in the service name to '\' characters */
692       string_replace( service, '/','\\');
693       argc--;
694       argv++;
695
696       if (count_chars(service,'\\') < 3)
697         {
698           usage(pname);
699           printf("\n%s: Not enough '\\' characters in service\n",service);
700           exit(1);
701         }
702
703       if (argc > 1 && (*argv[1] != '-'))
704         {
705           got_pass = True;
706           pstrcpy(password,argv[1]);  
707           memset(argv[1],'X',strlen(argv[1]));
708           argc--;
709           argv++;
710         }
711     }
712
713   while ((opt = 
714           getopt(argc, argv,"s:B:O:M:S:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
715     switch (opt)
716       {
717       case 'm':
718         max_protocol = interpret_protocol(optarg,max_protocol);
719         break;
720       case 'O':
721         pstrcpy(user_socket_options,optarg);
722         break;  
723       case 'S':
724         pstrcpy(desthost,optarg);
725         strupper(desthost);
726         nt_domain_logon = True;
727         break;
728       case 'B':
729         iface_set_default(NULL,optarg,NULL);
730         break;
731       case 'D':
732         pstrcpy(base_directory,optarg);
733         break;
734       case 'i':
735         pstrcpy(scope,optarg);
736         break;
737       case 'U':
738         {
739           char *lp;
740         pstrcpy(username,optarg);
741         if ((lp=strchr(username,'%')))
742           {
743             *lp = 0;
744             pstrcpy(password,lp+1);
745             got_pass = True;
746             memset(strchr(optarg,'%')+1,'X',strlen(password));
747           }
748         }
749             
750         break;
751       case 'W':
752         pstrcpy(workgroup,optarg);
753         break;
754       case 'E':
755         dbf = stderr;
756         break;
757       case 'I':
758         {
759           dest_ip = *interpret_addr2(optarg);
760           if (zero_ip(dest_ip)) exit(1);
761           have_ip = True;
762         }
763         break;
764       case 'n':
765         pstrcpy(global_myname,optarg);
766         break;
767       case 'N':
768         got_pass = True;
769         break;
770       case 'd':
771         if (*optarg == 'A')
772           DEBUGLEVEL = 10000;
773         else
774           DEBUGLEVEL = atoi(optarg);
775         break;
776       case 'l':
777         slprintf(debugf,sizeof(debugf)-1,"%s.client",optarg);
778         break;
779       case 'p':
780         port = atoi(optarg);
781         break;
782       case 'c':
783         cmdstr = optarg;
784         got_pass = True;
785         break;
786       case 'h':
787         usage(pname);
788         exit(0);
789         break;
790       case 's':
791         pstrcpy(servicesf, optarg);
792         break;
793       case 't':
794         pstrcpy(term_code, optarg);
795         break;
796       default:
797         usage(pname);
798         exit(1);
799       }
800
801   if (!*query_host && !*service)
802     {
803       usage(pname);
804       exit(1);
805     }
806
807
808   DEBUG( 3, ( "Client started (version %s)\n", VERSION ) );
809
810   if(!get_myname(myhostname,NULL))
811   {
812     DEBUG(0,("Failed to get my hostname.\n"));
813   }
814
815   if (!lp_load(servicesf,True,False,False)) {
816     fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf);
817   }
818
819   codepage_initialise(lp_client_code_page());
820
821   interpret_coding_system(term_code);
822
823   if (*workgroup == 0)
824     pstrcpy(workgroup,lp_workgroup());
825
826   load_interfaces();
827   get_myname((*global_myname)?NULL:global_myname,NULL);  
828   strupper(global_myname);
829
830   if (cli_open_sockets(port))
831     {
832       if (!process(base_directory))
833         {
834           close_sockets();
835           return(1);
836         }
837       close_sockets();
838     }
839   else
840     return(1);
841
842   return(0);
843 }