1 //
2 // anyRemote
3 // a wi-fi or bluetooth remote for your PC.
4 //
5 // Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
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 3 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21
22 #include <arpa/inet.h>
23 #include <ctype.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <netinet/in.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <time.h>
38 #include <unistd.h>
39
40 #include "common.h"
41 #include "utils.h"
42 #include "conf.h"
43 #include "dispatcher.h"
44 #include "sys_util.h"
45
46 extern char tmp[MAXMAXLEN];
47 extern int remoteOn;
48
49
50 /* Have issues with sockets if use execNoFork()
51 If some app started from anyRemote, then it keeps socket open even if anyRemote
52 is killed somehow. Have "ERROR: on binding 98->Address already in use" errors because of
53 socket CLOSE_WAIT state reported by /usr/sbin/lsof -i :<port>
54 !!! Therefore need to use fork !!!
55
56 int execNoFork(const char *cmd)
57 {
58 DEBUG2("execNoFork >%s<", cmd);
59
60 //printf("execute >%s< command\n",cmd);
61 #ifdef __cplusplus
62 int rc =
63 #endif
64 system(cmd);
65
66 //printf("command executed %d\n", rc);
67
68 //if (WIFSIGNALED(rc) && (WTERMSIG(rc) == SIGINT || WTERMSIG(rc) == SIGQUIT)) {
69 // printf("GOR ABORT in system?");
70 //}
71 //if(rc!=-1 && WIFEXITED(rc)) {
72 // rc = WEXITSTATUS(rc);
73 //}
74 //printf("exit from child %d\n", rc);
75 return 0;
76 }*/
77
execInFork(const char * cmd)78 static int execInFork(const char *cmd)
79 {
80 if (cmd == NULL) {
81 logger(L_ERR,"[EX]: Nothing to fork!");
82 return -1;
83 }
84
85 //DEBUG2("[EX]: execInFork >%s<", cmd);
86 int cpid = fork();
87 if (cpid < 0) {
88 logger(L_ERR,"[EX]: Can not fork!");
89 return -1;
90 }
91
92 if (cpid) { // father
93 int rf = 0;
94 waitpid(cpid,&rf,0);
95 if(rf!=-1 && WIFEXITED(rf)) {
96 rf = WEXITSTATUS(rf);
97 }
98 return rf;
99 }
100
101 // else - child
102
103 int rc = 0;
104 //logger("INF", "[EX]: Close port in child");
105
106 closePort(0);
107
108 rc = system(cmd);
109 if (rc != -1 && WIFEXITED(rc)) {
110 rc = WEXITSTATUS(rc);
111 }
112 exit(rc);
113 }
114
execCmdNoPipe(int subtype,const char * descr,const char * cmd,cmdParams * params)115 int execCmdNoPipe(int subtype, const char *descr, const char *cmd, cmdParams *params)
116 {
117 //logger(L_INF, "[EX]: Command: Exec");
118
119 if (cmd == NULL || strlen(cmd) == 0 || remoteOn != 1) {
120 logger(L_DBG, "[EX]: execCmdNoPipe null input or remote if OFF");
121 return EXIT_OK;
122 }
123 DEBUG2("[EX]: execCmdNoPipe >%s<", cmd);
124
125 char* p = (char*) calloc(strlen(cmd) + 2,1);
126 if (p == NULL) {
127 return EXIT_NOK;
128 }
129 strcpy(p,cmd);
130
131 // How to handle & inside file names ?
132 //if (!index(p, '&') ) {
133
134 int idx = strlen(cmd)-1;
135 while (idx > 0 && isspace(cmd[idx])) {
136 idx--;
137 }
138
139 if (cmd[idx] != '&') {
140 strcat(p, "&");
141 }
142
143 //int ret = execNoFork(p);
144 int ret = execInFork(p);
145
146 free(p);
147 return (ret == -1 ? EXIT_NOK : EXIT_OK);
148 }
149
150
151 #define RSIZE 512
152
153 /*
154 #define READ 0
155 #define WRITE 1
156
157 pid_t popen2(const char *command, int *infp, int *outfp)
158 {
159 int p_stdin[2], p_stdout[2];
160 pid_t pid;
161
162 if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) {
163 return -1;
164 }
165
166 pid = fork();
167
168 if (pid < 0) {
169
170 return pid;
171
172 } else if (pid == 0) { // child
173
174 close(p_stdin[WRITE]);
175 dup2(p_stdin[READ], STDIN_FILENO);
176
177 close(p_stdout[READ]);
178 dup2(p_stdout[WRITE], STDOUT_FILENO);
179
180 execl("/bin/sh", "sh", "-c", command, NULL);
181 ERROR2("[EX]: failed in popen2");
182 return -1;
183 }
184
185 if (infp == NULL) {
186 close(p_stdin[WRITE]);
187 } else {
188 *infp = p_stdin[WRITE];
189 }
190
191 if (outfp == NULL) {
192 close(p_stdout[READ]);
193 } else {
194 *outfp = p_stdout[READ];
195 }
196 DEBUG2("[EX]: popen2 >%d<", pid);
197 return pid;
198 }
199
200 this version does not send data until script is killed manually
201
202 char* executeCommandPipe(const char* exec, size_t* sz)
203 {
204 DEBUG2("[ ]: executeCommandPipe >%s<", exec);
205
206 int outfp;
207 pid_t pid = popen2(exec, NULL, &outfp);
208
209 fcntl(outfp, F_SETFL, O_NONBLOCK);
210 FILE* fp = fdopen(outfp,"r");
211 if (fp) {
212 setvbuf(fp, NULL, _IONBF, 0);
213 }
214
215 char* data = NULL;
216
217 void* temp[RSIZE];
218 size_t haveRead = 0;
219
220 int maxWait = getWaitTime();
221 time_t start = time(NULL);
222 DEBUG2("[ ]: executeCommandPipe WaitTime %d", maxWait);
223
224 while (1) {
225
226 DEBUG2("[ ]: executeCommandPipe -> read");
227 ssize_t num = read(outfp, temp, RSIZE);
228 DEBUG2("[ ]: executeCommandPipe <- read %d", num);
229
230 if (num == -1 && errno == EAGAIN) {
231 DEBUG2("[ ]: executeCommandPipe EAGAIN");
232 usleep(100000);
233 } else if (num > 0) {
234 data = realloc(data, haveRead+num+1);
235 if (!data) {
236 DEBUG2("[ ]: executeCommandPipe error on realloc %zd", haveRead+num);
237 return NULL;
238 }
239 memcpy(data+haveRead, temp, num);
240 haveRead += num;
241 *(data + haveRead) = '\0';
242 } else {
243 DEBUG2("[ ]: executeCommandPipe EOF");
244 break;
245 }
246
247 if (maxWait > 0) {
248 time_t now = time(NULL);
249 double elapsed = difftime(now, start);
250 if (elapsed > maxWait) {
251 WARNING2("[ ]: executeCommandPipe wait time exceeded");
252 kill(pid, SIGKILL);
253 break;
254 }
255 }
256 }
257 close(outfp);
258
259 DEBUG2("[ ]: executeCommandPipe got %zd bytes", haveRead);
260 (*sz) = haveRead;
261 return data;
262 }*/
263
264 /* this version also gets blocked if command runs forever
265
266 char* executeCommandPipe(const char* exec, size_t* sz)
267 {
268 DEBUG2("[ ]: executeCommandPipe >%s<", exec);
269
270 int outfp;
271 pid_t pid = popen2(exec, NULL, &outfp);
272
273 char* data = NULL;
274
275 void* temp[RSIZE];
276 size_t haveRead = 0;
277
278 while (1) {
279
280 DEBUG2("[ ]: executeCommandPipe -> read");
281 ssize_t num = read(outfp, temp, RSIZE);
282 DEBUG2("[ ]: executeCommandPipe <- read %d", num);
283
284 if (num <= 0) { // EOF or error
285 break;
286 } else if (num > 0) {
287 data = realloc(data, haveRead+num+1);
288 if (!data) {
289 DEBUG2("[ ]: executeCommandPipe error on realloc %zd", haveRead+num);
290 return NULL;
291 }
292 memcpy(data+haveRead, temp, num);
293 haveRead += num;
294 *(data + haveRead) = '\0';
295 }
296
297 //DEBUG2("[ ]: executeCommandPipe realloc %zd", haveRead);
298
299 if (num < RSIZE) {
300 break;
301 }
302 }
303 close(outfp);
304
305 DEBUG2("[ ]: executeCommandPipe got %zd bytes", haveRead);
306 (*sz) = haveRead;
307 return data;
308 }
309 */
310
311 #define RDR 0
312 #define WTR 1
313
314 static char bin_shell[] = "/bin/sh" ;
315 static char shell[] = "sh";
316 static char shflg[] = "-c";
317
318 //
319 // function non-reenterable
320 // (forked child will not close fd's opened by other popen_r's
321 //
popen_r(const char * cmd,pid_t * pid)322 static int popen_r(const char* cmd, pid_t* pid)
323 {
324 int p[2];
325 register int myside, yourside;
326
327 if (pipe(p) < 0) {
328 return -1;
329 }
330
331 myside = p[RDR];
332 yourside = p[WTR];
333
334 if( ((*pid) = fork()) == 0) {
335
336 // myside and yourside reverse roles in child
337
338 (void) close(myside);
339 (void) close(STDOUT_FILENO);
340 (void) fcntl(yourside, F_DUPFD, STDOUT_FILENO);
341 (void) close(yourside);
342 (void) execl(bin_shell, shell, shflg, cmd, (char *)0);
343 _exit(1);
344 }
345 if ((*pid) == -1) {
346 return -1;
347 }
348 (void) close(yourside);
349
350 return myside;
351 }
352
pclose_r(int fd,pid_t pid)353 static int pclose_r(int fd, pid_t pid)
354 {
355 register int r;
356 int status;
357 void (*hstat)(), (*istat)(), (*qstat)();
358
359 (void) close(fd);
360 istat = signal(SIGINT, SIG_IGN);
361 qstat = signal(SIGQUIT, SIG_IGN);
362 hstat = signal(SIGHUP, SIG_IGN);
363
364 // while the child is not done and no error has occured wait in the loop
365 while ((r = wait(&status)) != pid && (r != -1 || errno == EINTR)) {
366 usleep(1000);
367 }
368 if (r == -1) {
369 status = -1;
370 }
371
372 (void) signal(SIGINT, istat);
373 (void) signal(SIGQUIT, qstat);
374 (void) signal(SIGHUP, hstat);
375
376 return status;
377 }
378
379 // seems this version works
380
executeCommandPipe(const char * exec,size_t * sz)381 char* executeCommandPipe(const char* exec, size_t* sz)
382 {
383 DEBUG2("[ ]: executeCommandPipe >%s<", exec);
384
385 pid_t pid;
386 int fd = popen_r(exec, &pid);
387 if (fd < 0) {
388 logger(L_ERR, "[ ]: Error on popen()");
389 return NULL;
390 }
391
392 fcntl(fd, F_SETFL, O_NONBLOCK);
393
394 char* data = NULL;
395
396 void* temp[RSIZE];
397 size_t haveRead = 0;
398
399 int maxWait = getWaitTime();
400 time_t start = time(NULL);
401 if (maxWait >= 0) {
402 DEBUG2("[ ]: executeCommandPipe WaitTime %d", maxWait);
403 }
404
405 while (1) {
406
407 size_t num = read(fd, temp, RSIZE);
408
409 if (num == -1 && errno == EAGAIN) {
410 //DEBUG2("[ ]: executeCommandPipe EAGAIN");
411 usleep(1000);
412 } else if (num > 0) {
413
414 //DEBUG2("[ ]: executeCommandPipe read %d", num);
415 data = realloc(data, haveRead+num+1);
416 if (!data) {
417 DEBUG2("[ ]: executeCommandPipe error on realloc %zd", haveRead+num);
418 return NULL;
419 }
420 memcpy(data+haveRead, temp, num);
421 haveRead += num;
422 *(data + haveRead) = '\0';
423 } else {
424 break;
425 }
426
427 //DEBUG2("[ ]: executeCommandPipe realloc %zd", haveRead);
428
429 if (maxWait > 0) {
430 time_t now = time(NULL);
431 double elapsed = difftime(now, start);
432 if (elapsed > maxWait) {
433 WARNING2("[ ]: executeCommandPipe wait time exceeded");
434 kill(pid, SIGTERM);
435 break;
436 }
437 }
438 }
439
440 pclose_r(fd, pid);
441
442 DEBUG2("[ ]: executeCommandPipe got %zd bytes", haveRead);
443 (*sz) = haveRead;
444
445 return data;
446 }
447
448 /* this version is simple, but gets blocked if command runs forever
449
450 char* executeCommandPipe(const char* exec, size_t* sz)
451 {
452 DEBUG2("[ ]: executeCommandPipe >%s<", exec);
453
454 FILE *fp = popen(exec, "r");
455 if (fp == NULL) {
456 logger(L_ERR, "[EX]: Error on popen()");
457 return NULL;
458 }
459
460 char* data = NULL;
461
462 void* temp[RSIZE];
463 size_t haveRead = 0;
464
465 while (1) {
466 size_t num = fread(temp, 1, RSIZE, fp);
467
468 if (num > 0) {
469 data = realloc(data, haveRead+num+1);
470 if (!data) {
471 DEBUG2("[ ]: executeCommandPipe error on realloc %zd", haveRead+num);
472 return NULL;
473 }
474 memcpy(data+haveRead, temp, num);
475 haveRead += num;
476 *(data + haveRead) = '\0';
477 }
478
479 //DEBUG2("[ ]: executeCommandPipe realloc %zd", haveRead);
480
481 if (num < RSIZE) {
482 break;
483 }
484 }
485
486
487 //int status =
488
489 pclose(fp);
490
491 //if (status == -1) {
492 // Error reported by pclose()
493 //...
494 //} else {
495 // Use macros described under wait() to inspect `status' in order
496 // to determine success/failure of command executed by popen()
497 //...
498 //}
499
500 DEBUG2("[ ]: executeCommandPipe got %zd bytes", haveRead);
501 (*sz) = haveRead;
502 return data;
503 }*/
504
505
peerName(int peer,char * buf,int sz)506 void peerName(int peer, char* buf, int sz)
507 {
508 struct sockaddr_storage addr;
509 socklen_t len = sizeof(addr);
510
511 if (getpeername(peer, (struct sockaddr*)&addr, &len) == 0) {
512
513 // deal with both IPv4 and IPv6:
514 if (addr.ss_family == AF_INET) {
515 struct sockaddr_in *s = (struct sockaddr_in *)&addr;
516 inet_ntop(AF_INET, &s->sin_addr, buf, sz);
517 } else { // AF_INET6
518 struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
519 inet_ntop(AF_INET6, &s->sin6_addr, buf, sz);
520 }
521 }
522 }
523
524 static const char* dirTmp;
525
filterFiles(const struct dirent * dp)526 static int filterFiles(const struct dirent *dp)
527 {
528 string_t *path = stringNew(dirTmp);
529 stringAppend(path,"/");
530 stringAppend(path,dp->d_name);
531
532 struct stat buf;
533 int stat = lstat(path->str, &buf);
534 //printf("%d %s %s\n", stat,dp->d_name,strerror(errno));
535 return (stat == 0 && !S_ISDIR(buf.st_mode));
536 }
537
filterDirs(const struct dirent * dp)538 static int filterDirs(const struct dirent *dp)
539 {
540 string_t *path = stringNew(dirTmp);
541 stringAppend(path,"/");
542 stringAppend(path,dp->d_name);
543
544 struct stat buf;
545 int stat = lstat(path->str, &buf);
546 //printf("%d %s %s\n", stat,dp->d_name,strerror(errno));
547 return (stat == 0 && S_ISDIR(buf.st_mode));
548 }
549
550 // mimic "ls -F --quoting-style=shell" to little extent
executeDirListCommand(int type,const char * directory)551 string_t* executeDirListCommand(int type, const char* directory)
552 {
553 DEBUG2("[EX]: executeDirListCommand get listing of %s", directory);
554 struct stat buf;
555 int stat = lstat(directory, &buf);
556
557 if (stat < 0) {
558 DEBUG2("[EX]: executeDirListCommand can not get %s", directory);
559 return NULL;
560 }
561
562 if (S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) {
563
564 string_t *file = stringNew("");
565
566 if (type == ID_SET_ILIST) {
567 stringAppend(file,"file:");
568 }
569 stringAppend(file,directory);
570
571 return file;
572
573 } else if (S_ISDIR(buf.st_mode)) {
574
575 DEBUG2("[EX]: executeDirListCommand: directory");
576
577 dirTmp = directory; // small trick
578
579 struct dirent **namelist = NULL;
580
581 string_t *folders = stringNew("");
582
583 int comma = 0;
584
585 int n = scandir(directory, &namelist, filterDirs, alphasort);
586 if (n >= 0) {
587
588 int i = 0;
589 for( ; i<n; i++) {
590 //printf("%s\n", namelist[i]->d_name);
591
592 if (strcmp(namelist[i]->d_name,".") != 0) {
593 if (comma == 1) {
594 stringAppend(folders,",");
595 } else {
596 comma = 1;
597 }
598
599 if (type == ID_SET_ILIST) {
600 stringAppend(folders,"folder:");
601 }
602 stringAppend(folders,namelist[i]->d_name);
603 stringAppend(folders,"/");
604 }
605 free(namelist[i]);
606 }
607 free(namelist);
608 }
609 //DEBUG2("[EX]: executeDirListCommand: %s", folders->str);
610
611 n = scandir(directory, &namelist, filterFiles, alphasort);
612 if (n >= 0) {
613
614 int i = 0;
615 for( ; i<n; i++) {
616 //printf("%s\n", namelist[i]->d_name);
617
618 if (strcmp(namelist[i]->d_name,".") != 0) {
619 if (comma == 1) {
620 stringAppend(folders,",");
621 } else {
622 comma = 1;
623 }
624
625 if (type == ID_SET_ILIST) {
626 stringAppend(folders,"file:");
627 }
628 stringAppend(folders,namelist[i]->d_name);
629 }
630 free(namelist[i]);
631 }
632 free(namelist);
633 }
634 //DEBUG2("[EX]: executeDirListCommand: %s", folders->str);
635 return folders;
636
637 /* gets items unsorted
638 DIR *dirp = opendir(directory);
639
640 if (dirp) {
641
642 struct dirent *dp;
643
644 int fComma = 0;
645 int dComma = 0;
646 string_t *files = stringNew("");
647 string_t *folders = stringNew("");
648
649 string_t *file = stringNew("");
650
651 while ((dp=readdir(dirp))) {
652
653 if (strcmp(dp->d_name,".") == 0) continue;
654
655 //DEBUG2("[EX]: executeDirListCommand process %s", dp->d_name);
656
657 stringTruncate(file,0);
658 stringAppend(file,directory);
659 stringAppend(file,"/");
660 stringAppend(file,dp->d_name);
661
662 int stat = lstat(file->str, &buf);
663 if (stat < 0) continue;
664
665 if (S_ISDIR(buf.st_mode)) {
666
667 if (dComma == 1) {
668 stringAppend(folders,",");
669 } else {
670 dComma = 1;
671 }
672
673 if (type == ID_SET_ILIST) {
674 stringAppend(folders,"folder:");
675 }
676 stringAppend(folders,dp->d_name);
677 stringAppend(folders,"/");
678
679 } else //if (S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode))
680 {
681
682 if (fComma == 1) {
683 stringAppend(files,",");
684 } else {
685 fComma = 1;
686 }
687
688 if (type == ID_SET_ILIST) {
689 stringAppend(files,"file:");
690 }
691 stringAppend(files,dp->d_name);
692
693 }
694 }
695
696 stringFree(file, BOOL_YES);
697 closedir(dirp);
698
699 if (folders->len > 0) {
700 stringAppend(folders,",");
701 }
702 stringAppend(folders,files->str); // dirs, then files
703 stringFree(files, BOOL_YES);
704
705 //DEBUG2("[EX]: executeDirListCommand DONE");
706 return folders;
707 }*/
708 }
709 return NULL;
710 }
711
712 //////////////////////////////////////////////////////////////////////////////////
713 //
714 // IP address detection
715 //
716 //////////////////////////////////////////////////////////////////////////////////
717
get_iface_list(struct ifconf * ifconf)718 static int get_iface_list(struct ifconf *ifconf)
719 {
720 int sock, rval;
721
722 sock = socket(AF_INET,SOCK_STREAM,0);
723 if(sock < 0)
724 {
725 perror("socket");
726 return (-1);
727 }
728
729 if((rval = ioctl(sock, SIOCGIFCONF , (char*) ifconf )) < 0 ) {
730 perror("ioctl(SIOGIFCONF)");
731 }
732
733 close(sock);
734
735 return rval;
736 }
737
getLocalIP()738 string_t* getLocalIP()
739 {
740 struct ifreq ifreqs[20];
741 struct ifconf ifconf;
742
743 memset(&ifconf,0,sizeof(ifconf));
744 ifconf.ifc_buf = (char*) (ifreqs);
745 ifconf.ifc_len = sizeof(ifreqs);
746
747 if(get_iface_list(&ifconf) < 0) {
748 return NULL;
749 }
750 int nifaces = ifconf.ifc_len/sizeof(struct ifreq);
751
752 DEBUG2("[WS]: getLocalIP found %d interfaces", nifaces);
753
754 int i;
755 for(i = 0; i < nifaces; i++) {
756 DEBUG2("[WS]: interface #%d is %s", i, ifreqs[i].ifr_name);
757 static char* lo = "lo";
758 if (strcmp(lo,ifreqs[i].ifr_name) == 0) {
759 continue; // skip loopback
760 }
761 static char* loopback = "127.0.0.1";
762 if (strcmp(loopback, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr)) == 0) {
763 continue; // skip loopback
764 }
765 DEBUG2("[WS]: use local IP %s", inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
766 return stringNew(inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
767 }
768 return NULL;
769 }
770