Earlier fix for smbmount timing window was the wrong patch.
[tprouty/samba.git] / source / 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         int first_time = 1;
259
260         while (1)
261         {
262                 if ((fd = open(mount_point, O_RDONLY)) < 0)
263                 {
264                         DEBUG(0, ("smbmount: can't open %s\n", mount_point));
265                         break;
266                 }               
267
268                 /*
269                  * Call the ioctl even if we couldn't get a socket ...
270                  * there's no point in making smbfs wait for a timeout.
271                  */
272                 conn_options.fd = -1;
273                 if (res)
274                         conn_options.fd = Client;
275                 res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
276                 if (res != 0)
277                 {
278                         DEBUG(0, ("smbmount: ioctl failed, res=%d\n", res));
279                 }
280
281                 close_sockets();
282                 close(fd);
283                 /*
284                  * Close all open files if we haven't done so yet.
285                  */
286 #ifndef SMBFS_DEBUG
287                 if (!closed)
288                 {
289                         closed = 1;
290                         close_our_files();
291                 }
292 #endif
293
294                 if( first_time ) {
295         /*
296          * Create the background process after trying the mount.
297          * to avoid race conditions with automount and other processes.
298          */
299                         first_time = 0;
300                         daemonize();
301                 }
302
303                 /*
304                  * Wait for a signal from smbfs ...
305                  */
306                 CatchSignal(SIGUSR1, &usr1_handler);
307                 pause();
308                 DEBUG(0, ("smbmount: got signal, getting new socket\n"));
309
310                 res = cli_open_sockets(port);
311                 if (!res)
312                 {
313                         DEBUG(0, ("smbmount: can't open sockets\n"));
314                         continue;
315                 }
316
317                 res = mount_send_login(inbuf, outbuf);
318                 if (!res)
319                 {
320                         DEBUG(0, ("smbmount: login failed\n"));
321                 }
322         }
323         DEBUG(0, ("smbmount: exit\n"));
324         exit(1);
325 }
326
327 /****************************************************************************
328 mount smbfs
329 ****************************************************************************/
330 static void cmd_mount(char *inbuf,char *outbuf)
331 {
332         pstring mpoint;
333         pstring share_name;
334         pstring mount_command;
335         fstring buf;
336         int retval;
337         char mount_point[MAXPATHLEN+1];
338
339         if (!next_token(NULL, mpoint, NULL, sizeof(mpoint)))
340         {
341                 DEBUG(0,("You must supply a mount point\n"));
342                 return;
343         }
344
345         memset(mount_point, 0, sizeof(mount_point));
346
347         if (realpath(mpoint, mount_point) == NULL)
348         {
349                 DEBUG(0, ("Could not resolve mount point\n"));
350                 return;
351         }
352
353         /*
354          * Build the service name to report on the Unix side,
355          * converting '\' to '/' and ' ' to '_'.
356          */
357         pstrcpy(share_name, service);  
358         string_replace(share_name, '\\', '/');
359         string_replace(share_name, ' ', '_');
360
361         slprintf(mount_command, sizeof(mount_command)-1,"smbmnt %s -s %s", mount_point, share_name);
362
363         while(next_token(NULL, buf, NULL, sizeof(buf)))
364         {
365                 pstrcat(mount_command, " ");
366                 pstrcat(mount_command, buf);
367         }
368
369         DEBUG(3, ("mount command: %s\n", mount_command));
370
371         if ((retval = system(mount_command)) != 0)
372         {
373                 DEBUG(0,("mount failed\n"));
374                 exit(1);
375         }
376
377         send_fs_socket(mount_point, inbuf, outbuf);
378 }       
379
380
381 /* This defines the commands supported by this client */
382 struct
383 {
384   char *name;
385   void (*fn)();
386   char *description;
387 } commands[] = 
388 {
389   {"mount", cmd_mount, "<mount-point options> mount an smbfs file system"},
390   {"",NULL,NULL}
391 };
392
393
394 /*******************************************************************
395   lookup a command string in the list of commands, including 
396   abbreviations
397   ******************************************************************/
398 static int process_tok(fstring tok)
399 {
400   int i = 0, matches = 0;
401   int cmd=0;
402   int tok_len = strlen(tok);
403   
404   while (commands[i].fn != NULL)
405     {
406       if (strequal(commands[i].name,tok))
407         {
408           matches = 1;
409           cmd = i;
410           break;
411         }
412       else if (strnequal(commands[i].name, tok, tok_len))
413         {
414           matches++;
415           cmd = i;
416         }
417       i++;
418     }
419   
420   if (matches == 0)
421     return(-1);
422   else if (matches == 1)
423     return(cmd);
424   else
425     return(-2);
426 }
427
428 /****************************************************************************
429 help
430 ****************************************************************************/
431 void cmd_help(char *dum_in, char *dum_out)
432 {
433   int i=0,j;
434   fstring buf;
435
436   if (next_token(NULL,buf,NULL,sizeof(buf)))
437     {
438       if ((i = process_tok(buf)) >= 0)
439         DEBUG(0,("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description));                   
440     }
441   else
442     while (commands[i].description)
443       {
444         for (j=0; commands[i].description && (j<5); j++) {
445           DEBUG(0,("%-15s",commands[i].name));
446           i++;
447         }
448         DEBUG(0,("\n"));
449       }
450 }
451
452 /****************************************************************************
453 wait for keyboard activity, swallowing network packets
454 ****************************************************************************/
455 static void wait_keyboard(char *buffer)
456 {
457   fd_set fds;
458   int selrtn;
459   struct timeval timeout;
460   
461   while (1) 
462     {
463       extern int Client;
464       FD_ZERO(&fds);
465       FD_SET(Client,&fds);
466       FD_SET(fileno(stdin),&fds);
467
468       timeout.tv_sec = 20;
469       timeout.tv_usec = 0;
470       selrtn = sys_select(MAX(Client,fileno(stdin))+1,&fds,&timeout);
471       
472       if (FD_ISSET(fileno(stdin),&fds))
473         return;
474
475       /* We deliberately use receive_smb instead of
476          client_receive_smb as we want to receive
477          session keepalives and then drop them here.
478        */
479       if (FD_ISSET(Client,&fds))
480         receive_smb(Client,buffer,0);
481       
482       chkpath("\\",False);
483     }  
484 }
485
486
487 /****************************************************************************
488   process commands from the client
489 ****************************************************************************/
490 static BOOL process(char *base_directory)
491 {
492   extern FILE *dbf;
493   pstring line;
494   char *cmd;
495
496   char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
497   char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
498
499   if ((InBuffer == NULL) || (OutBuffer == NULL)) 
500     return(False);
501   
502   bzero(OutBuffer,smb_size);
503
504   if (!mount_send_login(InBuffer,OutBuffer))
505     return(False);
506
507   cmd = cmdstr;
508   if (cmd[0] != '\0') while (cmd[0] != '\0')
509     {
510       char *p;
511       fstring tok;
512       int i;
513
514       if ((p = strchr(cmd, ';')) == 0)
515         {
516           strncpy(line, cmd, 999);
517           line[1000] = '\0';
518           cmd += strlen(cmd);
519         }
520       else
521         {
522           if (p - cmd > 999) p = cmd + 999;
523           strncpy(line, cmd, p - cmd);
524           line[p - cmd] = '\0';
525           cmd = p + 1;
526         }
527
528       /* input language code to internal one */
529       CNV_INPUT (line);
530       
531       /* and get the first part of the command */
532       {
533         char *ptr = line;
534         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
535       }
536
537       if ((i = process_tok(tok)) >= 0)
538         commands[i].fn(InBuffer,OutBuffer);
539       else if (i == -2)
540         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
541       else
542         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
543     }
544   else while (!feof(stdin))
545     {
546       fstring tok;
547       int i;
548
549       bzero(OutBuffer,smb_size);
550
551       /* display a prompt */
552       DEBUG(0,("smb: %s> ", CNV_LANG(cur_dir)));
553       dbgflush();
554
555       wait_keyboard(InBuffer);
556   
557       /* and get a response */
558       if (!fgets(line,1000,stdin))
559         break;
560
561       /* input language code to internal one */
562       CNV_INPUT (line);
563
564       /* special case - first char is ! */
565       if (*line == '!')
566         {
567           system(line + 1);
568           continue;
569         }
570       
571       /* and get the first part of the command */
572       {
573         char *ptr = line;
574         if (!next_token(&ptr,tok,NULL,sizeof(tok))) continue;
575       }
576
577       if ((i = process_tok(tok)) >= 0)
578         commands[i].fn(InBuffer,OutBuffer);
579       else if (i == -2)
580         DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
581       else
582         DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
583     }
584   
585   cli_send_logout(InBuffer,OutBuffer);
586   return(True);
587 }
588
589 /****************************************************************************
590 usage on the program
591 ****************************************************************************/
592 static void usage(char *pname)
593 {
594   DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ",
595            pname));
596
597   DEBUG(0,("\nVersion %s\n",VERSION));
598   DEBUG(0,("\t-p port               connect to the specified port\n"));
599   DEBUG(0,("\t-d debuglevel         set the debuglevel\n"));
600   DEBUG(0,("\t-l log basename.      Basename for log/debug files\n"));
601   DEBUG(0,("\t-n netbios name.      Use this name as my netbios name\n"));
602   DEBUG(0,("\t-N                    don't ask for a password\n"));
603   DEBUG(0,("\t-m max protocol       set the max protocol level\n"));
604   DEBUG(0,("\t-I dest IP            use this IP to connect to\n"));
605   DEBUG(0,("\t-E                    write messages to stderr instead of stdout\n"));
606   DEBUG(0,("\t-U username           set the network username\n"));
607   DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
608   DEBUG(0,("\t-c command string     execute semicolon separated commands\n"));
609   DEBUG(0,("\t-t terminal code      terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n"));
610   DEBUG(0,("\t-D directory          start from directory\n"));
611   DEBUG(0,("\n"));
612 }
613
614 /****************************************************************************
615   main program
616 ****************************************************************************/
617  int main(int argc,char *argv[])
618 {
619   fstring base_directory;
620   char *pname = argv[0];
621   int opt;
622   extern FILE *dbf;
623   extern char *optarg;
624   extern int optind;
625   pstring query_host;
626   BOOL nt_domain_logon = False;
627   static pstring servicesf = CONFIGFILE;
628   pstring term_code;
629   char *p;
630
631 #ifdef KANJI
632   pstrcpy(term_code, KANJI);
633 #else /* KANJI */
634   *term_code = 0;
635 #endif /* KANJI */
636
637   *query_host = 0;
638   *base_directory = 0;
639
640   DEBUGLEVEL = 2;
641
642   setup_logging(pname,True);
643
644   TimeInit();
645   charset_initialise();
646
647   pid = (uint16)getpid();
648   vuid = (uint16)getuid();
649   mid = pid + 100;
650   myumask = umask(0);
651   umask(myumask);
652
653   if (getenv("USER"))
654   {
655     pstrcpy(username,getenv("USER"));
656
657     /* modification to support userid%passwd syntax in the USER var
658        25.Aug.97, jdblair@uab.edu */
659
660     if ((p=strchr(username,'%')))
661     {
662       *p = 0;
663       pstrcpy(password,p+1);
664       got_pass = True;
665       memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
666     }
667     strupper(username);
668   }
669
670  /* modification to support PASSWD environmental var
671   25.Aug.97, jdblair@uab.edu */
672
673   if (getenv("PASSWD"))
674     pstrcpy(password,getenv("PASSWD"));
675
676   if (*username == 0 && getenv("LOGNAME"))
677     {
678       pstrcpy(username,getenv("LOGNAME"));
679       strupper(username);
680     }
681
682   if (argc < 2)
683     {
684       usage(pname);
685       exit(1);
686     }
687   
688   if (*argv[1] != '-')
689     {
690
691       pstrcpy(service, argv[1]);  
692       /* Convert any '/' characters in the service name to '\' characters */
693       string_replace( service, '/','\\');
694       argc--;
695       argv++;
696
697       if (count_chars(service,'\\') < 3)
698         {
699           usage(pname);
700           printf("\n%s: Not enough '\\' characters in service\n",service);
701           exit(1);
702         }
703
704       if (argc > 1 && (*argv[1] != '-'))
705         {
706           got_pass = True;
707           pstrcpy(password,argv[1]);  
708           memset(argv[1],'X',strlen(argv[1]));
709           argc--;
710           argv++;
711         }
712     }
713
714   while ((opt = 
715           getopt(argc, argv,"s:B:O:M:S:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
716     switch (opt)
717       {
718       case 'm':
719         max_protocol = interpret_protocol(optarg,max_protocol);
720         break;
721       case 'O':
722         pstrcpy(user_socket_options,optarg);
723         break;  
724       case 'S':
725         pstrcpy(desthost,optarg);
726         strupper(desthost);
727         nt_domain_logon = True;
728         break;
729       case 'B':
730         iface_set_default(NULL,optarg,NULL);
731         break;
732       case 'D':
733         pstrcpy(base_directory,optarg);
734         break;
735       case 'i':
736         pstrcpy(scope,optarg);
737         break;
738       case 'U':
739         {
740           char *lp;
741         pstrcpy(username,optarg);
742         if ((lp=strchr(username,'%')))
743           {
744             *lp = 0;
745             pstrcpy(password,lp+1);
746             got_pass = True;
747             memset(strchr(optarg,'%')+1,'X',strlen(password));
748           }
749         }
750             
751         break;
752       case 'W':
753         pstrcpy(workgroup,optarg);
754         break;
755       case 'E':
756         dbf = stderr;
757         break;
758       case 'I':
759         {
760           dest_ip = *interpret_addr2(optarg);
761           if (zero_ip(dest_ip)) exit(1);
762           have_ip = True;
763         }
764         break;
765       case 'n':
766         pstrcpy(global_myname,optarg);
767         break;
768       case 'N':
769         got_pass = True;
770         break;
771       case 'd':
772         if (*optarg == 'A')
773           DEBUGLEVEL = 10000;
774         else
775           DEBUGLEVEL = atoi(optarg);
776         break;
777       case 'l':
778         slprintf(debugf,sizeof(debugf)-1,"%s.client",optarg);
779         break;
780       case 'p':
781         port = atoi(optarg);
782         break;
783       case 'c':
784         cmdstr = optarg;
785         got_pass = True;
786         break;
787       case 'h':
788         usage(pname);
789         exit(0);
790         break;
791       case 's':
792         pstrcpy(servicesf, optarg);
793         break;
794       case 't':
795         pstrcpy(term_code, optarg);
796         break;
797       default:
798         usage(pname);
799         exit(1);
800       }
801
802   if (!*query_host && !*service)
803     {
804       usage(pname);
805       exit(1);
806     }
807
808
809   DEBUG( 3, ( "Client started (version %s)\n", VERSION ) );
810
811   if(!get_myname(myhostname,NULL))
812   {
813     DEBUG(0,("Failed to get my hostname.\n"));
814   }
815
816   if (!lp_load(servicesf,True,False,False)) {
817     fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf);
818   }
819
820   codepage_initialise(lp_client_code_page());
821
822   interpret_coding_system(term_code);
823
824   if (*workgroup == 0)
825     pstrcpy(workgroup,lp_workgroup());
826
827   load_interfaces();
828   get_myname((*global_myname)?NULL:global_myname,NULL);  
829   strupper(global_myname);
830
831   if (cli_open_sockets(port))
832     {
833       if (!process(base_directory))
834         {
835           close_sockets();
836           return(1);
837         }
838       close_sockets();
839     }
840   else
841     return(1);
842
843   return(0);
844 }