os_dep.c (10689B)
1 /* os_dep.c 2 * (c) 2002 Mikulas Patocka 3 * This file is a part of the Links program, released under GPL. 4 */ 5 6 #include <fcntl.h> 7 #include <limits.h> 8 #include <sys/ioctl.h> 9 #include <sys/socket.h> 10 #include <unistd.h> 11 12 #include "links.h" 13 14 int page_size = 4096; 15 16 int 17 is_safe_in_shell(unsigned char c) 18 { 19 return c == '@' || c == '+' || c == '-' || c == '.' || c == ',' 20 || c == '=' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') 21 || c == '_' || (c >= 'a' && c <= 'z'); 22 } 23 24 static inline int 25 is_safe_in_file(unsigned char c) 26 { 27 return !(c < ' ' || c == '"' || c == '*' || c == '/' || c == ':' 28 || c == '<' || c == '>' || c == '\\' || c == '|' || c >= 0x80); 29 } 30 31 static inline int 32 is_safe_in_url(unsigned char c) 33 { 34 return is_safe_in_shell(c) || c == ':' || c == '/' || c >= 0x80; 35 } 36 37 void 38 check_shell_security(unsigned char **cmd) 39 { 40 unsigned char *c = *cmd; 41 while (*c) { 42 if (!is_safe_in_shell(*c)) 43 *c = '_'; 44 c++; 45 } 46 } 47 48 void 49 check_filename(unsigned char **file) 50 { 51 unsigned char *c = *file; 52 while (*c) { 53 if (!is_safe_in_file(*c)) 54 *c = '_'; 55 c++; 56 } 57 } 58 59 int 60 check_shell_url(unsigned char *url) 61 { 62 while (*url) { 63 if (!is_safe_in_url(*url)) 64 return -1; 65 url++; 66 } 67 return 0; 68 } 69 70 unsigned char * 71 escape_path(char *path) 72 { 73 unsigned char *result; 74 size_t i; 75 if (strchr(path, '"')) 76 return stracpy(cast_uchar path); 77 for (i = 0; path[i]; i++) 78 if (!is_safe_in_url(path[i])) 79 goto do_esc; 80 return stracpy(cast_uchar path); 81 do_esc: 82 result = stracpy(cast_uchar "\""); 83 add_to_strn(&result, cast_uchar path); 84 add_to_strn(&result, cast_uchar "\""); 85 return result; 86 } 87 88 static int 89 get_e(const char *env) 90 { 91 const char *v; 92 if ((v = getenv(env))) 93 return atoi(v); 94 return 0; 95 } 96 97 void 98 init_page_size(void) 99 { 100 long getpg = -1; 101 if (getpg < 0) 102 getpg = getpagesize(); 103 if (getpg > 0 && !(getpg & (getpg - 1))) 104 page_size = (int)getpg; 105 } 106 107 void 108 do_signal(int sig, void (*handler)(int)) 109 { 110 errno = 0; 111 while (signal(sig, handler) == SIG_ERR && errno == EINTR) 112 errno = 0; 113 } 114 115 void 116 ignore_signals(void) 117 { 118 do_signal(SIGPIPE, SIG_IGN); 119 #ifdef SIGXFSZ 120 do_signal(SIGXFSZ, SIG_IGN); 121 #endif 122 } 123 124 uttime 125 get_absolute_time(void) 126 { 127 struct timeval tv; 128 int rs; 129 EINTRLOOP(rs, gettimeofday(&tv, NULL)); 130 if (rs) 131 fatal_exit("gettimeofday failed: %d", errno); 132 return (uttime)tv.tv_sec * 1000 + (unsigned)tv.tv_usec / 1000; 133 } 134 135 uttime 136 get_time(void) 137 { 138 #if defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC) 139 struct timespec ts; 140 int rs; 141 #if defined(CLOCK_MONOTONIC_RAW) 142 EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC_RAW, &ts)); 143 if (!rs) 144 return (uttime)ts.tv_sec * 1000 145 + (unsigned)ts.tv_nsec / 1000000; 146 #endif 147 #if defined(CLOCK_MONOTONIC) 148 EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC, &ts)); 149 if (!rs) 150 return (uttime)ts.tv_sec * 1000 151 + (unsigned)ts.tv_nsec / 1000000; 152 #endif 153 #endif 154 return get_absolute_time(); 155 } 156 157 static unsigned char *clipboard = NULL; 158 159 void 160 os_free_clipboard(void) 161 { 162 free(clipboard); 163 clipboard = NULL; 164 } 165 166 /* Terminal size */ 167 168 static void (*terminal_resize_callback)(int, int); 169 170 #ifdef SIGWINCH 171 static void 172 sigwinch(void *s) 173 { 174 int cur_xsize, cur_ysize; 175 get_terminal_size(&cur_xsize, &cur_ysize); 176 terminal_resize_callback(cur_xsize, cur_ysize); 177 } 178 #endif 179 180 void 181 handle_terminal_resize(void (*fn)(int, int), int *x, int *y) 182 { 183 terminal_resize_callback = fn; 184 get_terminal_size(x, y); 185 #if defined(SIGWINCH) 186 install_signal_handler(SIGWINCH, sigwinch, NULL, 0); 187 #endif 188 } 189 190 void 191 unhandle_terminal_resize(void) 192 { 193 #if defined(SIGWINCH) 194 install_signal_handler(SIGWINCH, NULL, NULL, 0); 195 #endif 196 } 197 198 void 199 get_terminal_size(int *x, int *y) 200 { 201 int rs = -1; 202 #ifdef TIOCGWINSZ 203 struct winsize ws; 204 EINTRLOOP(rs, ioctl(1, TIOCGWINSZ, &ws)); 205 #endif 206 if ((rs == -1 207 #ifdef TIOCGWINSZ 208 || !(*x = ws.ws_col) 209 #endif 210 ) 211 && !(*x = get_e("COLUMNS"))) { 212 *x = 80; 213 } 214 if ((rs == -1 215 #ifdef TIOCGWINSZ 216 || !(*y = ws.ws_row) 217 #endif 218 ) 219 && !(*y = get_e("LINES"))) { 220 *y = 24; 221 } 222 } 223 224 static void 225 new_fd_cloexec(int fd) 226 { 227 int rs; 228 EINTRLOOP(rs, fcntl(fd, F_SETFD, FD_CLOEXEC)); 229 } 230 231 static void 232 new_fd_bin(int fd) 233 { 234 new_fd_cloexec(fd); 235 } 236 237 /* Pipe */ 238 239 void 240 set_nonblock(int fd) 241 { 242 #ifdef O_NONBLOCK 243 int rs; 244 EINTRLOOP(rs, fcntl(fd, F_SETFL, O_NONBLOCK)); 245 #elif defined(FIONBIO) 246 int rs; 247 int on = 1; 248 EINTRLOOP(rs, ioctl(fd, FIONBIO, &on)); 249 #endif 250 } 251 252 static int 253 cleanup_fds(void) 254 { 255 #ifdef ENFILE 256 if (errno == ENFILE) 257 return abort_background_connections(); 258 #endif 259 #ifdef EMFILE 260 if (errno == EMFILE) 261 return abort_background_connections(); 262 #endif 263 return 0; 264 } 265 266 int 267 c_pipe(int fd[2]) 268 { 269 int r; 270 do { 271 EINTRLOOP(r, pipe(fd)); 272 if (!r) 273 new_fd_bin(fd[0]), new_fd_bin(fd[1]); 274 } while (r == -1 && cleanup_fds()); 275 return r; 276 } 277 278 int 279 c_dup(int oh) 280 { 281 int h; 282 do { 283 EINTRLOOP(h, dup(oh)); 284 if (h != -1) 285 new_fd_cloexec(h); 286 } while (h == -1 && cleanup_fds()); 287 return h; 288 } 289 290 int 291 c_socket(int d, int t, int p) 292 { 293 int h = socket(d, t, p); 294 295 if (h == -1) 296 die("socket()\n"); 297 298 if (fcntl(h, F_SETFD, FD_CLOEXEC) == -1) 299 die("c_socket(): fcntl()\n"); 300 301 return h; 302 } 303 304 int 305 c_accept(int sh, struct sockaddr *addr, socklen_t *addrlen) 306 { 307 int h; 308 do { 309 EINTRLOOP(h, accept(sh, addr, addrlen)); 310 if (h != -1) 311 new_fd_cloexec(h); 312 } while (h == -1 && cleanup_fds()); 313 return h; 314 } 315 316 int 317 c_open(unsigned char *path, int flags) 318 { 319 int h; 320 do { 321 EINTRLOOP(h, open(cast_const_char path, flags)); 322 if (h != -1) 323 new_fd_bin(h); 324 } while (h == -1 && cleanup_fds()); 325 return h; 326 } 327 328 int 329 c_open3(unsigned char *path, int flags, int mode) 330 { 331 int h; 332 do { 333 EINTRLOOP(h, open(cast_const_char path, flags, mode)); 334 if (h != -1) 335 new_fd_bin(h); 336 } while (h == -1 && cleanup_fds()); 337 return h; 338 } 339 340 DIR * 341 c_opendir(unsigned char *path) 342 { 343 DIR *d; 344 do { 345 ENULLLOOP(d, opendir(cast_const_char path)); 346 if (d) { 347 int h; 348 EINTRLOOP(h, dirfd(d)); 349 if (h != -1) 350 new_fd_cloexec(h); 351 } 352 } while (!d && cleanup_fds()); 353 return d; 354 } 355 356 /* Exec */ 357 358 int 359 is_screen(void) 360 { 361 static int xt = -1; 362 if (xt == -1) 363 xt = !!getenv("STY"); 364 return xt; 365 } 366 367 int 368 is_xterm(void) 369 { 370 static int xt = -1; 371 if (xt == -1) 372 xt = getenv("DISPLAY") && *(char *)getenv("DISPLAY"); 373 return xt; 374 } 375 376 void 377 close_fork_tty(void) 378 { 379 struct terminal *t = NULL; 380 struct list_head *lt; 381 struct download *d = NULL; 382 struct list_head *ld; 383 struct connection *c = NULL; 384 struct list_head *lc; 385 struct k_conn *k = NULL; 386 struct list_head *lk; 387 int rs; 388 EINTRLOOP(rs, close(signal_pipe[0])); 389 EINTRLOOP(rs, close(signal_pipe[1])); 390 if (terminal_pipe[1] != -1) 391 EINTRLOOP(rs, close(terminal_pipe[1])); 392 foreach (struct terminal, t, lt, terminals) { 393 if (t->fdin > 0) 394 EINTRLOOP(rs, close(t->fdin)); 395 if (t->handle_to_close >= 0) 396 EINTRLOOP(rs, close(t->handle_to_close)); 397 } 398 foreach (struct download, d, ld, downloads) 399 if (d->handle > 0) 400 EINTRLOOP(rs, close(d->handle)); 401 foreach (struct connection, c, lc, queue) { 402 if (c->sock1 >= 0) 403 EINTRLOOP(rs, close(c->sock1)); 404 if (c->sock2 >= 0) 405 EINTRLOOP(rs, close(c->sock2)); 406 } 407 foreach (struct k_conn, k, lk, keepalive_connections) 408 EINTRLOOP(rs, close(k->conn)); 409 } 410 411 unsigned char * 412 os_conv_to_external_path(unsigned char *file, unsigned char *prog) 413 { 414 return stracpy(file); 415 } 416 417 unsigned char * 418 os_fixup_external_program(unsigned char *prog) 419 { 420 return stracpy(prog); 421 } 422 423 /* UNIX */ 424 int 425 exe(char *path, int fg) 426 { 427 #ifdef SIGCHLD 428 do_signal(SIGCHLD, SIG_DFL); 429 #endif 430 do_signal(SIGPIPE, SIG_DFL); 431 #ifdef SIGXFSZ 432 do_signal(SIGXFSZ, SIG_DFL); 433 #endif 434 #ifdef SIGTSTP 435 do_signal(SIGTSTP, SIG_DFL); 436 #endif 437 #ifdef SIGCONT 438 do_signal(SIGCONT, SIG_DFL); 439 #endif 440 #ifdef SIGWINCH 441 do_signal(SIGWINCH, SIG_DFL); 442 #endif 443 return system(path); 444 } 445 446 /* clipboard -> links */ 447 unsigned char * 448 get_clipboard_text(struct terminal *term) 449 { 450 if (!clipboard) 451 return NULL; 452 return stracpy(clipboard); 453 } 454 455 /* links -> clipboard */ 456 void 457 set_clipboard_text(struct terminal *term, unsigned char *data) 458 { 459 free(clipboard); 460 clipboard = stracpy(data); 461 } 462 463 int 464 clipboard_support(struct terminal *term) 465 { 466 return 0; 467 } 468 469 void 470 set_window_title(unsigned char *title) 471 { 472 /* !!! FIXME */ 473 } 474 475 unsigned char * 476 get_window_title(void) 477 { 478 /* !!! FIXME */ 479 return NULL; 480 } 481 482 /* Threads */ 483 484 int 485 start_thread(void (*fn)(void *, int), void *ptr, int l, int counted) 486 { 487 int p[2]; 488 pid_t f; 489 int rs; 490 if (c_pipe(p) < 0) 491 return -1; 492 EINTRLOOP(f, fork()); 493 if (!f) { 494 close_fork_tty(); 495 EINTRLOOP(rs, close(p[0])); 496 fn(ptr, p[1]); 497 EINTRLOOP(rs, (int)write(p[1], "x", 1)); 498 EINTRLOOP(rs, close(p[1])); 499 _exit(0); 500 } 501 if (f == -1) { 502 EINTRLOOP(rs, close(p[0])); 503 EINTRLOOP(rs, close(p[1])); 504 return -1; 505 } 506 EINTRLOOP(rs, close(p[1])); 507 return p[0]; 508 } 509 510 static void 511 exec_new_links(struct terminal *term, unsigned char *xterm, unsigned char *exe, 512 unsigned char *param) 513 { 514 unsigned char *str; 515 str = stracpy(cast_uchar ""); 516 if (*xterm) { 517 add_to_strn(&str, xterm); 518 add_to_strn(&str, cast_uchar " "); 519 } 520 add_to_strn(&str, exe); 521 add_to_strn(&str, cast_uchar " "); 522 add_to_strn(&str, param); 523 exec_on_terminal(term, str, cast_uchar "", 2); 524 free(str); 525 } 526 527 static unsigned char * 528 links_xterm(void) 529 { 530 unsigned char *xterm; 531 if (!(xterm = cast_uchar getenv("LINKS_XTERM"))) { 532 xterm = cast_uchar "xterm -e"; 533 } 534 return xterm; 535 } 536 537 static int 538 open_in_new_xterm(struct terminal *term, unsigned char *exe, 539 unsigned char *param) 540 { 541 exec_new_links(term, links_xterm(), exe, param); 542 return 0; 543 } 544 545 static int 546 open_in_new_screen(struct terminal *term, unsigned char *exe, 547 unsigned char *param) 548 { 549 exec_new_links(term, cast_uchar "screen", exe, param); 550 return 0; 551 } 552 553 static const struct { 554 int env; 555 int (*open_window_fn)(struct terminal *term, unsigned char *, 556 unsigned char *); 557 unsigned char *text; 558 unsigned char *hk; 559 } oinw[] = { 560 {ENV_XWIN, open_in_new_xterm, TEXT_(T_XTERM), TEXT_(T_HK_XTERM) }, 561 { ENV_SCREEN, open_in_new_screen, TEXT_(T_SCREEN), TEXT_(T_HK_SCREEN)}, 562 }; 563 564 struct open_in_new * 565 get_open_in_new(int environment) 566 { 567 int i; 568 struct open_in_new *oin = NULL; 569 int noin = 0; 570 if (anonymous) 571 return NULL; 572 for (i = 0; i < (int)array_elements(oinw); i++) 573 if ((environment & oinw[i].env) == oinw[i].env) { 574 if ((unsigned)noin 575 > INT_MAX / sizeof(struct open_in_new) - 2) 576 overalloc(); 577 oin = xrealloc(oin, 578 (noin + 2) * sizeof(struct open_in_new)); 579 oin[noin].text = oinw[i].text; 580 oin[noin].hk = oinw[i].hk; 581 oin[noin].open_window_fn = &oinw[i].open_window_fn; 582 noin++; 583 oin[noin].text = NULL; 584 oin[noin].hk = NULL; 585 oin[noin].open_window_fn = NULL; 586 } 587 return oin; 588 } 589 590 void 591 os_detach_console(void) 592 { 593 #if !defined(NO_FORK_ON_EXIT) 594 pid_t rp; 595 EINTRLOOP(rp, fork()); 596 if (!rp) 597 reinit_child(); 598 if (rp > 0) 599 _exit(0); 600 #endif 601 }