VirtualBox

source: vbox/trunk/src/VBox/RDP/client/rdesktop.c@ 30798

最後變更 在這個檔案從30798是 26034,由 vboxsync 提交於 15 年 前

VBOX_VENDOR, VBOX_PRODUCT

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 35.1 KB
 
1/* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Entrypoint and utility functions
4 Copyright (C) Matthew Chapman 1999-2008
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21/*
22 * Sun GPL Disclaimer: For the avoidance of doubt, except that if any license choice
23 * other than GPL or LGPL is available it will apply instead, Sun elects to use only
24 * the General Public License version 2 (GPLv2) at this time for any software where
25 * a choice of GPL license versions is made available with the language indicating
26 * that GPLv2 or any later version may be used, or where a choice of which version
27 * of the GPL is applied is otherwise unspecified.
28 */
29
30#include <stdarg.h> /* va_list va_start va_end */
31#include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
32#include <fcntl.h> /* open */
33#include <pwd.h> /* getpwuid */
34#include <termios.h> /* tcgetattr tcsetattr */
35#include <sys/stat.h> /* stat */
36#include <sys/time.h> /* gettimeofday */
37#include <sys/times.h> /* times */
38#include <ctype.h> /* toupper */
39#include <errno.h>
40#include "rdesktop.h"
41
42#ifdef VBOX
43# include <VBox/version.h>
44#endif
45
46#ifdef HAVE_LOCALE_H
47#include <locale.h>
48#endif
49#ifdef HAVE_ICONV
50#ifdef HAVE_LANGINFO_H
51#include <langinfo.h>
52#endif
53#endif
54
55#ifdef EGD_SOCKET
56#include <sys/types.h>
57#include <sys/socket.h> /* socket connect */
58#include <sys/un.h> /* sockaddr_un */
59#endif
60
61#include "ssl.h"
62
63char g_title[64] = "";
64char g_username[64];
65char g_hostname[16];
66char g_keymapname[PATH_MAX] = "";
67unsigned int g_keylayout = 0x409; /* Defaults to US keyboard layout */
68int g_keyboard_type = 0x4; /* Defaults to US keyboard layout */
69int g_keyboard_subtype = 0x0; /* Defaults to US keyboard layout */
70int g_keyboard_functionkeys = 0xc; /* Defaults to US keyboard layout */
71
72int g_width = 800; /* width is special: If 0, the
73 geometry will be fetched from
74 _NET_WORKAREA. If negative,
75 absolute value specifies the
76 percent of the whole screen. */
77int g_height = 600;
78int g_xpos = 0;
79int g_ypos = 0;
80int g_pos = 0; /* 0 position unspecified,
81 1 specified,
82 2 xpos neg,
83 4 ypos neg */
84extern int g_tcp_port_rdp;
85int g_server_depth = -1;
86int g_win_button_size = 0; /* If zero, disable single app mode */
87RD_BOOL g_bitmap_compression = True;
88RD_BOOL g_sendmotion = True;
89RD_BOOL g_bitmap_cache = True;
90RD_BOOL g_bitmap_cache_persist_enable = False;
91RD_BOOL g_bitmap_cache_precache = True;
92RD_BOOL g_encryption = True;
93RD_BOOL g_packet_encryption = True;
94RD_BOOL g_desktop_save = True; /* desktop save order */
95RD_BOOL g_polygon_ellipse_orders = True; /* polygon / ellipse orders */
96RD_BOOL g_fullscreen = False;
97RD_BOOL g_grab_keyboard = True;
98RD_BOOL g_hide_decorations = False;
99RD_BOOL g_use_rdp5 = True;
100RD_BOOL g_rdpclip = True;
101RD_BOOL g_console_session = False;
102#ifndef VBOX
103RD_BOOL g_numlock_sync = False;
104#else /* VBOX */
105/* Always use numlock synchronization with VRDP. */
106RD_BOOL g_numlock_sync = True;
107#endif /* VBOX */
108RD_BOOL g_lspci_enabled = False;
109RD_BOOL g_owncolmap = False;
110RD_BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
111RD_BOOL g_seamless_rdp = False;
112uint32 g_embed_wnd;
113uint32 g_rdp5_performanceflags =
114 RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
115/* Session Directory redirection */
116RD_BOOL g_redirect = False;
117char g_redirect_server[64];
118char g_redirect_domain[16];
119char g_redirect_password[64];
120char g_redirect_username[64];
121char g_redirect_cookie[128];
122uint32 g_redirect_flags = 0;
123
124#ifdef WITH_RDPSND
125RD_BOOL g_rdpsnd = False;
126#endif
127
128#ifdef WITH_RDPUSB
129RD_BOOL g_rdpusb = False;
130#endif
131
132#ifdef HAVE_ICONV
133char g_codepage[16] = "";
134#endif
135
136extern RDPDR_DEVICE g_rdpdr_device[];
137extern uint32 g_num_devices;
138extern char *g_rdpdr_clientname;
139
140#ifdef RDP2VNC
141extern int rfb_port;
142extern int defer_time;
143void
144rdp2vnc_connect(char *server, uint32 flags, char *domain, char *password,
145 char *shell, char *directory);
146#endif
147/* Display usage information */
148static void
149usage(char *program)
150{
151 fprintf(stderr, "rdesktop: A Remote Desktop Protocol client.\n");
152 fprintf(stderr, "Version " VERSION ". Copyright (C) 1999-2008 Matthew Chapman.\n");
153 fprintf(stderr, "Modified for VirtualBox by " VBOX_VENDOR "\n");
154 fprintf(stderr, "See http://www.rdesktop.org/ for more information.\n\n");
155
156 fprintf(stderr, "Usage: %s [options] server[:port]\n", program);
157#ifdef RDP2VNC
158 fprintf(stderr, " -V: vnc port\n");
159 fprintf(stderr, " -Q: defer time (ms)\n");
160#endif
161 fprintf(stderr, " -u: user name\n");
162 fprintf(stderr, " -d: domain\n");
163 fprintf(stderr, " -s: shell\n");
164 fprintf(stderr, " -c: working directory\n");
165 fprintf(stderr, " -p: password (- to prompt)\n");
166 fprintf(stderr, " -n: client hostname\n");
167 fprintf(stderr, " -k: keyboard layout on server (en-us, de, sv, etc.)\n");
168 fprintf(stderr, " -g: desktop geometry (WxH)\n");
169 fprintf(stderr, " -f: full-screen mode\n");
170 fprintf(stderr, " -b: force bitmap updates\n");
171#ifdef HAVE_ICONV
172 fprintf(stderr, " -L: local codepage\n");
173#endif
174 fprintf(stderr, " -A: enable SeamlessRDP mode\n");
175 fprintf(stderr, " -B: use BackingStore of X-server (if available)\n");
176 fprintf(stderr, " -e: disable encryption (French TS)\n");
177 fprintf(stderr, " -E: disable encryption from client to server\n");
178 fprintf(stderr, " -m: do not send motion events\n");
179 fprintf(stderr, " -C: use private colour map\n");
180 fprintf(stderr, " -D: hide window manager decorations\n");
181 fprintf(stderr, " -K: keep window manager key bindings\n");
182 fprintf(stderr, " -S: caption button size (single application mode)\n");
183 fprintf(stderr, " -T: window title\n");
184 fprintf(stderr, " -N: enable numlock syncronization\n");
185 fprintf(stderr, " -X: embed into another window with a given id.\n");
186 fprintf(stderr, " -a: connection colour depth\n");
187 fprintf(stderr, " -z: enable rdp compression\n");
188 fprintf(stderr, " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex nr.)\n");
189 fprintf(stderr, " -P: use persistent bitmap caching\n");
190 fprintf(stderr, " -r: enable specified device redirection (this flag can be repeated)\n");
191 fprintf(stderr,
192 " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n");
193 fprintf(stderr, " or COM1=/dev/ttyS0,COM2=/dev/ttyS1\n");
194 fprintf(stderr,
195 " '-r disk:floppy=/mnt/floppy': enable redirection of /mnt/floppy to 'floppy' share\n");
196 fprintf(stderr, " or 'floppy=/mnt/floppy,cdrom=/mnt/cdrom'\n");
197 fprintf(stderr, " '-r clientname=<client name>': Set the client name displayed\n");
198 fprintf(stderr, " for redirected disks\n");
199 fprintf(stderr,
200 " '-r lptport:LPT1=/dev/lp0': enable parallel redirection of /dev/lp0 to LPT1\n");
201 fprintf(stderr, " or LPT1=/dev/lp0,LPT2=/dev/lp1\n");
202 fprintf(stderr, " '-r printer:mydeskjet': enable printer redirection\n");
203 fprintf(stderr,
204 " or mydeskjet=\"HP LaserJet IIIP\" to enter server driver as well\n");
205#ifdef WITH_RDPSND
206 fprintf(stderr,
207 " '-r sound:[local[:driver[:device]]|off|remote]': enable sound redirection\n");
208 fprintf(stderr, " remote would leave sound on server\n");
209 fprintf(stderr, " available drivers for 'local':\n");
210 rdpsnd_show_help();
211#endif
212#ifdef WITH_RDPUSB
213 fprintf(stderr,
214 " '-r usb': enable USB redirection\n");
215#endif
216 fprintf(stderr,
217 " '-r clipboard:[off|PRIMARYCLIPBOARD|CLIPBOARD]': enable clipboard\n");
218 fprintf(stderr, " redirection.\n");
219 fprintf(stderr,
220 " 'PRIMARYCLIPBOARD' looks at both PRIMARY and CLIPBOARD\n");
221 fprintf(stderr, " when sending data to server.\n");
222 fprintf(stderr, " 'CLIPBOARD' looks at only CLIPBOARD.\n");
223#ifdef WITH_SCARD
224 fprintf(stderr, " '-r scard[:\"Scard Name\"=\"Alias Name[;Vendor Name]\"[,...]]\n");
225 fprintf(stderr, " example: -r scard:\"eToken PRO 00 00\"=\"AKS ifdh 0\"\n");
226 fprintf(stderr,
227 " \"eToken PRO 00 00\" -> Device in Linux/Unix enviroment\n");
228 fprintf(stderr,
229 " \"AKS ifdh 0\" -> Device shown in Windows enviroment \n");
230 fprintf(stderr, " example: -r scard:\"eToken PRO 00 00\"=\"AKS ifdh 0;AKS\"\n");
231 fprintf(stderr,
232 " \"eToken PRO 00 00\" -> Device in Linux/Unix enviroment\n");
233 fprintf(stderr,
234 " \"AKS ifdh 0\" -> Device shown in Windows enviroment \n");
235 fprintf(stderr,
236 " \"AKS\" -> Device vendor name \n");
237#endif
238 fprintf(stderr, " -0: attach to console\n");
239 fprintf(stderr, " -4: use RDP version 4\n");
240 fprintf(stderr, " -5: use RDP version 5 (default)\n");
241}
242
243static void
244print_disconnect_reason(uint16 reason)
245{
246 char *text;
247
248 switch (reason)
249 {
250 case exDiscReasonNoInfo:
251 text = "No information available";
252 break;
253
254 case exDiscReasonAPIInitiatedDisconnect:
255 text = "Server initiated disconnect";
256 break;
257
258 case exDiscReasonAPIInitiatedLogoff:
259 text = "Server initiated logoff";
260 break;
261
262 case exDiscReasonServerIdleTimeout:
263 text = "Server idle timeout reached";
264 break;
265
266 case exDiscReasonServerLogonTimeout:
267 text = "Server logon timeout reached";
268 break;
269
270 case exDiscReasonReplacedByOtherConnection:
271 text = "The session was replaced";
272 break;
273
274 case exDiscReasonOutOfMemory:
275 text = "The server is out of memory";
276 break;
277
278 case exDiscReasonServerDeniedConnection:
279 text = "The server denied the connection";
280 break;
281
282 case exDiscReasonServerDeniedConnectionFips:
283 text = "The server denied the connection for security reason";
284 break;
285
286 case exDiscReasonLicenseInternal:
287 text = "Internal licensing error";
288 break;
289
290 case exDiscReasonLicenseNoLicenseServer:
291 text = "No license server available";
292 break;
293
294 case exDiscReasonLicenseNoLicense:
295 text = "No valid license available";
296 break;
297
298 case exDiscReasonLicenseErrClientMsg:
299 text = "Invalid licensing message";
300 break;
301
302 case exDiscReasonLicenseHwidDoesntMatchLicense:
303 text = "Hardware id doesn't match software license";
304 break;
305
306 case exDiscReasonLicenseErrClientLicense:
307 text = "Client license error";
308 break;
309
310 case exDiscReasonLicenseCantFinishProtocol:
311 text = "Network error during licensing protocol";
312 break;
313
314 case exDiscReasonLicenseClientEndedProtocol:
315 text = "Licensing protocol was not completed";
316 break;
317
318 case exDiscReasonLicenseErrClientEncryption:
319 text = "Incorrect client license enryption";
320 break;
321
322 case exDiscReasonLicenseCantUpgradeLicense:
323 text = "Can't upgrade license";
324 break;
325
326 case exDiscReasonLicenseNoRemoteConnections:
327 text = "The server is not licensed to accept remote connections";
328 break;
329
330 default:
331 if (reason > 0x1000 && reason < 0x7fff)
332 {
333 text = "Internal protocol error";
334 }
335 else
336 {
337 text = "Unknown reason";
338 }
339 }
340 fprintf(stderr, "disconnect: %s.\n", text);
341}
342
343static void
344rdesktop_reset_state(void)
345{
346 rdp_reset_state();
347}
348
349static RD_BOOL
350read_password(char *password, int size)
351{
352 struct termios tios;
353 RD_BOOL ret = False;
354 int istty = 0;
355 char *p;
356
357 if (tcgetattr(STDIN_FILENO, &tios) == 0)
358 {
359 fprintf(stderr, "Password: ");
360 tios.c_lflag &= ~ECHO;
361 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
362 istty = 1;
363 }
364
365 if (fgets(password, size, stdin) != NULL)
366 {
367 ret = True;
368
369 /* strip final newline */
370 p = strchr(password, '\n');
371 if (p != NULL)
372 *p = 0;
373 }
374
375 if (istty)
376 {
377 tios.c_lflag |= ECHO;
378 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
379 fprintf(stderr, "\n");
380 }
381
382 return ret;
383}
384
385static void
386parse_server_and_port(char *server)
387{
388 char *p;
389#ifdef IPv6
390 int addr_colons;
391#endif
392
393#ifdef IPv6
394 p = server;
395 addr_colons = 0;
396 while (*p)
397 if (*p++ == ':')
398 addr_colons++;
399 if (addr_colons >= 2)
400 {
401 /* numeric IPv6 style address format - [1:2:3::4]:port */
402 p = strchr(server, ']');
403 if (*server == '[' && p != NULL)
404 {
405 if (*(p + 1) == ':' && *(p + 2) != '\0')
406 g_tcp_port_rdp = strtol(p + 2, NULL, 10);
407 /* remove the port number and brackets from the address */
408 *p = '\0';
409 strncpy(server, server + 1, strlen(server));
410 }
411 }
412 else
413 {
414 /* dns name or IPv4 style address format - server.example.com:port or 1.2.3.4:port */
415 p = strchr(server, ':');
416 if (p != NULL)
417 {
418 g_tcp_port_rdp = strtol(p + 1, NULL, 10);
419 *p = 0;
420 }
421 }
422#else /* no IPv6 support */
423 p = strchr(server, ':');
424 if (p != NULL)
425 {
426 g_tcp_port_rdp = strtol(p + 1, NULL, 10);
427 *p = 0;
428 }
429#endif /* IPv6 */
430
431}
432
433/* Client program */
434int
435main(int argc, char *argv[])
436{
437 char server[64];
438 char fullhostname[64];
439 char domain[16];
440 char password[64];
441 char shell[256];
442 char directory[256];
443 RD_BOOL prompt_password, deactivated;
444 struct passwd *pw;
445 uint32 flags, ext_disc_reason = 0;
446 char *p;
447 int c;
448 char *locale = NULL;
449 int username_option = 0;
450 RD_BOOL geometry_option = False;
451 int run_count = 0; /* Session Directory support */
452 RD_BOOL continue_connect = True; /* Session Directory support */
453#ifdef WITH_RDPSND
454 char *rdpsnd_optarg = NULL;
455#endif
456
457#ifdef HAVE_LOCALE_H
458 /* Set locale according to environment */
459 locale = setlocale(LC_ALL, "");
460 if (locale)
461 {
462 locale = xstrdup(locale);
463 }
464
465#endif
466 flags = RDP_LOGON_NORMAL;
467 prompt_password = False;
468 domain[0] = password[0] = shell[0] = directory[0] = 0;
469 g_embed_wnd = 0;
470
471 g_num_devices = 0;
472
473#ifdef RDP2VNC
474#define VNCOPT "V:Q:"
475#else
476#define VNCOPT
477#endif
478
479 while ((c = getopt(argc, argv,
480 VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
481 {
482 switch (c)
483 {
484#ifdef RDP2VNC
485 case 'V':
486 rfb_port = strtol(optarg, NULL, 10);
487 if (rfb_port < 100)
488 rfb_port += 5900;
489 break;
490
491 case 'Q':
492 defer_time = strtol(optarg, NULL, 10);
493 if (defer_time < 0)
494 defer_time = 0;
495 break;
496#endif
497
498 case 'A':
499 g_seamless_rdp = True;
500 break;
501
502 case 'u':
503 STRNCPY(g_username, optarg, sizeof(g_username));
504 username_option = 1;
505 break;
506
507 case 'L':
508#ifdef HAVE_ICONV
509 STRNCPY(g_codepage, optarg, sizeof(g_codepage));
510#else
511 error("iconv support not available\n");
512#endif
513 break;
514
515 case 'd':
516 STRNCPY(domain, optarg, sizeof(domain));
517 break;
518
519 case 's':
520 STRNCPY(shell, optarg, sizeof(shell));
521 break;
522
523 case 'c':
524 STRNCPY(directory, optarg, sizeof(directory));
525 break;
526
527 case 'p':
528 if ((optarg[0] == '-') && (optarg[1] == 0))
529 {
530 prompt_password = True;
531 break;
532 }
533
534 STRNCPY(password, optarg, sizeof(password));
535 flags |= RDP_LOGON_AUTO;
536
537 /* try to overwrite argument so it won't appear in ps */
538 p = optarg;
539 while (*p)
540 *(p++) = 'X';
541 break;
542
543 case 'n':
544 STRNCPY(g_hostname, optarg, sizeof(g_hostname));
545 break;
546
547 case 'k':
548 STRNCPY(g_keymapname, optarg, sizeof(g_keymapname));
549 break;
550
551 case 'g':
552 geometry_option = True;
553 g_fullscreen = False;
554 if (!strcmp(optarg, "workarea"))
555 {
556 g_width = g_height = 0;
557 break;
558 }
559
560 g_width = strtol(optarg, &p, 10);
561 if (g_width <= 0)
562 {
563 error("invalid geometry\n");
564 return 1;
565 }
566
567 if (*p == 'x')
568 g_height = strtol(p + 1, &p, 10);
569
570 if (g_height <= 0)
571 {
572 error("invalid geometry\n");
573 return 1;
574 }
575
576 if (*p == '%')
577 {
578 g_width = -g_width;
579 p++;
580 }
581
582 if (*p == '+' || *p == '-')
583 {
584 g_pos |= (*p == '-') ? 2 : 1;
585 g_xpos = strtol(p, &p, 10);
586
587 }
588 if (*p == '+' || *p == '-')
589 {
590 g_pos |= (*p == '-') ? 4 : 1;
591 g_ypos = strtol(p, NULL, 10);
592 }
593
594 break;
595
596 case 'f':
597 g_fullscreen = True;
598 break;
599
600 case 'b':
601 g_bitmap_cache = False;
602 break;
603
604 case 'B':
605 g_ownbackstore = False;
606 break;
607
608 case 'e':
609 g_encryption = False;
610 break;
611 case 'E':
612 g_packet_encryption = False;
613 break;
614 case 'm':
615 g_sendmotion = False;
616 break;
617
618 case 'C':
619 g_owncolmap = True;
620 break;
621
622 case 'D':
623 g_hide_decorations = True;
624 break;
625
626 case 'K':
627 g_grab_keyboard = False;
628 break;
629
630 case 'S':
631 if (!strcmp(optarg, "standard"))
632 {
633 g_win_button_size = 18;
634 break;
635 }
636
637 g_win_button_size = strtol(optarg, &p, 10);
638
639 if (*p)
640 {
641 error("invalid button size\n");
642 return 1;
643 }
644
645 break;
646
647 case 'T':
648 STRNCPY(g_title, optarg, sizeof(g_title));
649 break;
650
651 case 'N':
652 g_numlock_sync = True;
653 break;
654
655 case 'X':
656 g_embed_wnd = strtol(optarg, NULL, 0);
657 break;
658
659 case 'a':
660 g_server_depth = strtol(optarg, NULL, 10);
661 if (g_server_depth != 8 &&
662 g_server_depth != 16 &&
663 g_server_depth != 15 && g_server_depth != 24
664 && g_server_depth != 32)
665 {
666 error("Invalid server colour depth.\n");
667 return 1;
668 }
669 break;
670
671 case 'z':
672 DEBUG(("rdp compression enabled\n"));
673 flags |= (RDP_LOGON_COMPRESSION | RDP_LOGON_COMPRESSION2);
674 break;
675
676 case 'x':
677 if (str_startswith(optarg, "m")) /* modem */
678 {
679 g_rdp5_performanceflags =
680 RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG |
681 RDP5_NO_MENUANIMATIONS | RDP5_NO_THEMING;
682 }
683 else if (str_startswith(optarg, "b")) /* broadband */
684 {
685 g_rdp5_performanceflags = RDP5_NO_WALLPAPER;
686 }
687 else if (str_startswith(optarg, "l")) /* lan */
688 {
689 g_rdp5_performanceflags = RDP5_DISABLE_NOTHING;
690 }
691 else
692 {
693 g_rdp5_performanceflags = strtol(optarg, NULL, 16);
694 }
695 break;
696
697 case 'P':
698 g_bitmap_cache_persist_enable = True;
699 break;
700
701 case 'r':
702
703 if (str_startswith(optarg, "sound"))
704 {
705 optarg += 5;
706
707 if (*optarg == ':')
708 {
709 optarg++;
710 while ((p = next_arg(optarg, ',')))
711 {
712 if (str_startswith(optarg, "remote"))
713 flags |= RDP_LOGON_LEAVE_AUDIO;
714
715 if (str_startswith(optarg, "local"))
716#ifdef WITH_RDPSND
717 {
718 rdpsnd_optarg =
719 next_arg(optarg, ':');
720 g_rdpsnd = True;
721 }
722
723#else
724 warning("Not compiled with sound support\n");
725#endif
726
727 if (str_startswith(optarg, "off"))
728#ifdef WITH_RDPSND
729 g_rdpsnd = False;
730#else
731 warning("Not compiled with sound support\n");
732#endif
733
734 optarg = p;
735 }
736 }
737 else
738 {
739#ifdef WITH_RDPSND
740 g_rdpsnd = True;
741#else
742 warning("Not compiled with sound support\n");
743#endif
744 }
745 }
746 else if (str_startswith(optarg, "usb"))
747 {
748#ifdef WITH_RDPUSB
749 g_rdpusb = True;
750#else
751 warning("Not compiled with USB support\n");
752#endif
753 }
754 else if (str_startswith(optarg, "disk"))
755 {
756 /* -r disk:h:=/mnt/floppy */
757 disk_enum_devices(&g_num_devices, optarg + 4);
758 }
759 else if (str_startswith(optarg, "comport"))
760 {
761 serial_enum_devices(&g_num_devices, optarg + 7);
762 }
763 else if (str_startswith(optarg, "lspci"))
764 {
765 g_lspci_enabled = True;
766 }
767 else if (str_startswith(optarg, "lptport"))
768 {
769 parallel_enum_devices(&g_num_devices, optarg + 7);
770 }
771 else if (str_startswith(optarg, "printer"))
772 {
773 printer_enum_devices(&g_num_devices, optarg + 7);
774 }
775 else if (str_startswith(optarg, "clientname"))
776 {
777 g_rdpdr_clientname = xmalloc(strlen(optarg + 11) + 1);
778 strcpy(g_rdpdr_clientname, optarg + 11);
779 }
780 else if (str_startswith(optarg, "clipboard"))
781 {
782 optarg += 9;
783
784 if (*optarg == ':')
785 {
786 optarg++;
787
788 if (str_startswith(optarg, "off"))
789 g_rdpclip = False;
790 else
791 cliprdr_set_mode(optarg);
792 }
793 else
794 g_rdpclip = True;
795 }
796 else if (strncmp("scard", optarg, 5) == 0)
797 {
798#ifdef WITH_SCARD
799 scard_enum_devices(&g_num_devices, optarg + 5);
800#else
801 warning("Not compiled with smartcard support\n");
802#endif
803 }
804 else
805 {
806 warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound, clipboard, scard\n");
807 }
808 break;
809
810 case '0':
811 g_console_session = True;
812 break;
813
814 case '4':
815 g_use_rdp5 = False;
816 break;
817
818 case '5':
819 g_use_rdp5 = True;
820 break;
821
822 case 'h':
823 case '?':
824 default:
825 usage(argv[0]);
826 return 1;
827 }
828 }
829
830 if (argc - optind != 1)
831 {
832 usage(argv[0]);
833 return 1;
834 }
835
836 STRNCPY(server, argv[optind], sizeof(server));
837 parse_server_and_port(server);
838
839 if (g_seamless_rdp)
840 {
841 if (g_win_button_size)
842 {
843 error("You cannot use -S and -A at the same time\n");
844 return 1;
845 }
846 g_rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG;
847 if (geometry_option)
848 {
849 error("You cannot use -g and -A at the same time\n");
850 return 1;
851 }
852 if (g_fullscreen)
853 {
854 error("You cannot use -f and -A at the same time\n");
855 return 1;
856 }
857 if (g_hide_decorations)
858 {
859 error("You cannot use -D and -A at the same time\n");
860 return 1;
861 }
862 if (g_embed_wnd)
863 {
864 error("You cannot use -X and -A at the same time\n");
865 return 1;
866 }
867 if (!g_use_rdp5)
868 {
869 error("You cannot use -4 and -A at the same time\n");
870 return 1;
871 }
872 g_width = -100;
873 g_grab_keyboard = False;
874 }
875
876 if (!username_option)
877 {
878 pw = getpwuid(getuid());
879 if ((pw == NULL) || (pw->pw_name == NULL))
880 {
881 error("could not determine username, use -u\n");
882 return 1;
883 }
884
885 STRNCPY(g_username, pw->pw_name, sizeof(g_username));
886 }
887
888#ifdef HAVE_ICONV
889 if (g_codepage[0] == 0)
890 {
891 if (setlocale(LC_CTYPE, ""))
892 {
893 STRNCPY(g_codepage, nl_langinfo(CODESET), sizeof(g_codepage));
894 }
895 else
896 {
897 STRNCPY(g_codepage, DEFAULT_CODEPAGE, sizeof(g_codepage));
898 }
899 }
900#endif
901
902 if (g_hostname[0] == 0)
903 {
904 if (gethostname(fullhostname, sizeof(fullhostname)) == -1)
905 {
906 error("could not determine local hostname, use -n\n");
907 return 1;
908 }
909
910 p = strchr(fullhostname, '.');
911 if (p != NULL)
912 *p = 0;
913
914 STRNCPY(g_hostname, fullhostname, sizeof(g_hostname));
915 }
916
917 if (g_keymapname[0] == 0)
918 {
919 if (locale && xkeymap_from_locale(locale))
920 {
921 fprintf(stderr, "Autoselected keyboard map %s\n", g_keymapname);
922 }
923 else
924 {
925 STRNCPY(g_keymapname, "en-us", sizeof(g_keymapname));
926 }
927 }
928 if (locale)
929 xfree(locale);
930
931
932 if (prompt_password && read_password(password, sizeof(password)))
933 flags |= RDP_LOGON_AUTO;
934
935 if (g_title[0] == 0)
936 {
937 strcpy(g_title, "rdesktop - ");
938 strncat(g_title, server, sizeof(g_title) - sizeof("rdesktop - "));
939 }
940
941#ifdef RDP2VNC
942 rdp2vnc_connect(server, flags, domain, password, shell, directory);
943 return 0;
944#else
945
946 if (!ui_init())
947 return 1;
948
949#ifdef WITH_RDPSND
950 if (g_rdpsnd)
951 {
952 if (!rdpsnd_init(rdpsnd_optarg))
953 {
954 warning("Initializing sound-support failed!\n");
955 }
956 }
957#endif
958
959#ifdef WITH_RDPUSB
960 if (g_rdpusb)
961 rdpusb_init();
962#endif
963
964 if (g_lspci_enabled)
965 lspci_init();
966
967 rdpdr_init();
968
969 while (run_count < 2 && continue_connect) /* add support for Session Directory; only reconnect once */
970 {
971 if (run_count == 0)
972 {
973 if (!rdp_connect(server, flags, domain, password, shell, directory))
974 return 1;
975 }
976 else if (!rdp_reconnect
977 (server, flags, domain, password, shell, directory, g_redirect_cookie))
978 return 1;
979
980 /* By setting encryption to False here, we have an encrypted login
981 packet but unencrypted transfer of other packets */
982 if (!g_packet_encryption)
983 g_encryption = False;
984
985
986 DEBUG(("Connection successful.\n"));
987 memset(password, 0, sizeof(password));
988
989 if (run_count == 0)
990 if (!ui_create_window())
991 continue_connect = False;
992
993 if (continue_connect)
994 rdp_main_loop(&deactivated, &ext_disc_reason);
995
996 DEBUG(("Disconnecting...\n"));
997 rdp_disconnect();
998
999 if ((g_redirect == True) && (run_count == 0)) /* Support for Session Directory */
1000 {
1001 /* reset state of major globals */
1002 rdesktop_reset_state();
1003
1004 STRNCPY(domain, g_redirect_domain, sizeof(domain));
1005 STRNCPY(g_username, g_redirect_username, sizeof(g_username));
1006 STRNCPY(password, g_redirect_password, sizeof(password));
1007 STRNCPY(server, g_redirect_server, sizeof(server));
1008 flags |= RDP_LOGON_AUTO;
1009
1010 g_redirect = False;
1011 }
1012 else
1013 {
1014 continue_connect = False;
1015 ui_destroy_window();
1016 break;
1017 }
1018
1019 run_count++;
1020 }
1021
1022 cache_save_state();
1023 ui_deinit();
1024
1025#ifdef WITH_RDPUSB
1026 if (g_rdpusb)
1027 rdpusb_close();
1028#endif
1029
1030 if (ext_disc_reason >= 2)
1031 print_disconnect_reason(ext_disc_reason);
1032
1033 if (deactivated)
1034 {
1035 /* clean disconnect */
1036 return 0;
1037 }
1038 else
1039 {
1040 if (ext_disc_reason == exDiscReasonAPIInitiatedDisconnect
1041 || ext_disc_reason == exDiscReasonAPIInitiatedLogoff)
1042 {
1043 /* not so clean disconnect, but nothing to worry about */
1044 return 0;
1045 }
1046 else
1047 {
1048 /* return error */
1049 return 2;
1050 }
1051 }
1052
1053#endif
1054
1055}
1056
1057#ifdef EGD_SOCKET
1058/* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
1059static RD_BOOL
1060generate_random_egd(uint8 * buf)
1061{
1062 struct sockaddr_un addr;
1063 RD_BOOL ret = False;
1064 int fd;
1065
1066 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1067 if (fd == -1)
1068 return False;
1069
1070 addr.sun_family = AF_UNIX;
1071 memcpy(addr.sun_path, EGD_SOCKET, sizeof(EGD_SOCKET));
1072 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
1073 goto err;
1074
1075 /* PRNGD and EGD use a simple communications protocol */
1076 buf[0] = 1; /* Non-blocking (similar to /dev/urandom) */
1077 buf[1] = 32; /* Number of requested random bytes */
1078 if (write(fd, buf, 2) != 2)
1079 goto err;
1080
1081 if ((read(fd, buf, 1) != 1) || (buf[0] == 0)) /* Available? */
1082 goto err;
1083
1084 if (read(fd, buf, 32) != 32)
1085 goto err;
1086
1087 ret = True;
1088
1089 err:
1090 close(fd);
1091 return ret;
1092}
1093#endif
1094
1095/* Generate a 32-byte random for the secure transport code. */
1096void
1097generate_random(uint8 * random)
1098{
1099 struct stat st;
1100 struct tms tmsbuf;
1101 SSL_MD5 md5;
1102 uint32 *r;
1103 int fd, n;
1104
1105 /* If we have a kernel random device, try that first */
1106 if (((fd = open("/dev/urandom", O_RDONLY)) != -1)
1107 || ((fd = open("/dev/random", O_RDONLY)) != -1))
1108 {
1109 n = read(fd, random, 32);
1110 close(fd);
1111 if (n == 32)
1112 return;
1113 }
1114
1115#ifdef EGD_SOCKET
1116 /* As a second preference use an EGD */
1117 if (generate_random_egd(random))
1118 return;
1119#endif
1120
1121 /* Otherwise use whatever entropy we can gather - ideas welcome. */
1122 r = (uint32 *) random;
1123 r[0] = (getpid()) | (getppid() << 16);
1124 r[1] = (getuid()) | (getgid() << 16);
1125 r[2] = times(&tmsbuf); /* system uptime (clocks) */
1126 gettimeofday((struct timeval *) &r[3], NULL); /* sec and usec */
1127 stat("/tmp", &st);
1128 r[5] = st.st_atime;
1129 r[6] = st.st_mtime;
1130 r[7] = st.st_ctime;
1131
1132 /* Hash both halves with MD5 to obscure possible patterns */
1133 ssl_md5_init(&md5);
1134 ssl_md5_update(&md5, random, 16);
1135 ssl_md5_final(&md5, random);
1136 ssl_md5_update(&md5, random + 16, 16);
1137 ssl_md5_final(&md5, random + 16);
1138}
1139
1140/* malloc; exit if out of memory */
1141void *
1142xmalloc(int size)
1143{
1144 void *mem = malloc(size);
1145 if (mem == NULL)
1146 {
1147 error("xmalloc %d\n", size);
1148 exit(1);
1149 }
1150 return mem;
1151}
1152
1153/* Exit on NULL pointer. Use to verify result from XGetImage etc */
1154void
1155exit_if_null(void *ptr)
1156{
1157 if (ptr == NULL)
1158 {
1159 error("unexpected null pointer. Out of memory?\n");
1160 exit(1);
1161 }
1162}
1163
1164/* strdup */
1165char *
1166xstrdup(const char *s)
1167{
1168 char *mem = strdup(s);
1169 if (mem == NULL)
1170 {
1171 perror("strdup");
1172 exit(1);
1173 }
1174 return mem;
1175}
1176
1177/* realloc; exit if out of memory */
1178void *
1179xrealloc(void *oldmem, size_t size)
1180{
1181 void *mem;
1182
1183 if (size == 0)
1184 size = 1;
1185 mem = realloc(oldmem, size);
1186 if (mem == NULL)
1187 {
1188 error("xrealloc %ld\n", size);
1189 exit(1);
1190 }
1191 return mem;
1192}
1193
1194/* free */
1195void
1196xfree(void *mem)
1197{
1198 free(mem);
1199}
1200
1201/* report an error */
1202void
1203error(char *format, ...)
1204{
1205 va_list ap;
1206
1207 fprintf(stderr, "ERROR: ");
1208
1209 va_start(ap, format);
1210 vfprintf(stderr, format, ap);
1211 va_end(ap);
1212}
1213
1214/* report a warning */
1215void
1216warning(char *format, ...)
1217{
1218 va_list ap;
1219
1220 fprintf(stderr, "WARNING: ");
1221
1222 va_start(ap, format);
1223 vfprintf(stderr, format, ap);
1224 va_end(ap);
1225}
1226
1227/* report an unimplemented protocol feature */
1228void
1229unimpl(char *format, ...)
1230{
1231 va_list ap;
1232
1233 fprintf(stderr, "NOT IMPLEMENTED: ");
1234
1235 va_start(ap, format);
1236 vfprintf(stderr, format, ap);
1237 va_end(ap);
1238}
1239
1240/* produce a hex dump */
1241void
1242hexdump(unsigned char *p, unsigned int len)
1243{
1244 unsigned char *line = p;
1245 int i, thisline, offset = 0;
1246
1247 while (offset < len)
1248 {
1249 printf("%04x ", offset);
1250 thisline = len - offset;
1251 if (thisline > 16)
1252 thisline = 16;
1253
1254 for (i = 0; i < thisline; i++)
1255 printf("%02x ", line[i]);
1256
1257 for (; i < 16; i++)
1258 printf(" ");
1259
1260 for (i = 0; i < thisline; i++)
1261 printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
1262
1263 printf("\n");
1264 offset += thisline;
1265 line += thisline;
1266 }
1267}
1268
1269/*
1270 input: src is the string we look in for needle.
1271 Needle may be escaped by a backslash, in
1272 that case we ignore that particular needle.
1273 return value: returns next src pointer, for
1274 succesive executions, like in a while loop
1275 if retval is 0, then there are no more args.
1276 pitfalls:
1277 src is modified. 0x00 chars are inserted to
1278 terminate strings.
1279 return val, points on the next val chr after ins
1280 0x00
1281
1282 example usage:
1283 while( (pos = next_arg( optarg, ',')) ){
1284 printf("%s\n",optarg);
1285 optarg=pos;
1286 }
1287
1288*/
1289char *
1290next_arg(char *src, char needle)
1291{
1292 char *nextval;
1293 char *p;
1294 char *mvp = 0;
1295
1296 /* EOS */
1297 if (*src == (char) 0x00)
1298 return 0;
1299
1300 p = src;
1301 /* skip escaped needles */
1302 while ((nextval = strchr(p, needle)))
1303 {
1304 mvp = nextval - 1;
1305 /* found backslashed needle */
1306 if (*mvp == '\\' && (mvp > src))
1307 {
1308 /* move string one to the left */
1309 while (*(mvp + 1) != (char) 0x00)
1310 {
1311 *mvp = *(mvp + 1);
1312 mvp++;
1313 }
1314 *mvp = (char) 0x00;
1315 p = nextval;
1316 }
1317 else
1318 {
1319 p = nextval + 1;
1320 break;
1321 }
1322
1323 }
1324
1325 /* more args available */
1326 if (nextval)
1327 {
1328 *nextval = (char) 0x00;
1329 return ++nextval;
1330 }
1331
1332 /* no more args after this, jump to EOS */
1333 nextval = src + strlen(src);
1334 return nextval;
1335}
1336
1337
1338void
1339toupper_str(char *p)
1340{
1341 while (*p)
1342 {
1343 if ((*p >= 'a') && (*p <= 'z'))
1344 *p = toupper((int) *p);
1345 p++;
1346 }
1347}
1348
1349
1350RD_BOOL
1351str_startswith(const char *s, const char *prefix)
1352{
1353 return (strncmp(s, prefix, strlen(prefix)) == 0);
1354}
1355
1356
1357/* Split input into lines, and call linehandler for each
1358 line. Incomplete lines are saved in the rest variable, which should
1359 initially point to NULL. When linehandler returns False, stop and
1360 return False. Otherwise, return True. */
1361RD_BOOL
1362str_handle_lines(const char *input, char **rest, str_handle_lines_t linehandler, void *data)
1363{
1364 char *buf, *p;
1365 char *oldrest;
1366 size_t inputlen;
1367 size_t buflen;
1368 size_t restlen = 0;
1369 RD_BOOL ret = True;
1370
1371 /* Copy data to buffer */
1372 inputlen = strlen(input);
1373 if (*rest)
1374 restlen = strlen(*rest);
1375 buflen = restlen + inputlen + 1;
1376 buf = (char *) xmalloc(buflen);
1377 buf[0] = '\0';
1378 if (*rest)
1379 STRNCPY(buf, *rest, buflen);
1380 strncat(buf, input, inputlen);
1381 p = buf;
1382
1383 while (1)
1384 {
1385 char *newline = strchr(p, '\n');
1386 if (newline)
1387 {
1388 *newline = '\0';
1389 if (!linehandler(p, data))
1390 {
1391 p = newline + 1;
1392 ret = False;
1393 break;
1394 }
1395 p = newline + 1;
1396 }
1397 else
1398 {
1399 break;
1400
1401 }
1402 }
1403
1404 /* Save in rest */
1405 oldrest = *rest;
1406 restlen = buf + buflen - p;
1407 *rest = (char *) xmalloc(restlen);
1408 STRNCPY((*rest), p, restlen);
1409 xfree(oldrest);
1410
1411 xfree(buf);
1412 return ret;
1413}
1414
1415/* Execute the program specified by argv. For each line in
1416 stdout/stderr output, call linehandler. Returns false on failure. */
1417RD_BOOL
1418subprocess(char *const argv[], str_handle_lines_t linehandler, void *data)
1419{
1420 pid_t child;
1421 int fd[2];
1422 int n = 1;
1423 char output[256];
1424 char *rest = NULL;
1425
1426 if (pipe(fd) < 0)
1427 {
1428 perror("pipe");
1429 return False;
1430 }
1431
1432 if ((child = fork()) < 0)
1433 {
1434 perror("fork");
1435 return False;
1436 }
1437
1438 /* Child */
1439 if (child == 0)
1440 {
1441 /* Close read end */
1442 close(fd[0]);
1443
1444 /* Redirect stdout and stderr to pipe */
1445 dup2(fd[1], 1);
1446 dup2(fd[1], 2);
1447
1448 /* Execute */
1449 execvp(argv[0], argv);
1450 perror("Error executing child");
1451 _exit(128);
1452 }
1453
1454 /* Parent. Close write end. */
1455 close(fd[1]);
1456 while (n > 0)
1457 {
1458 n = read(fd[0], output, 255);
1459 output[n] = '\0';
1460 str_handle_lines(output, &rest, linehandler, data);
1461 }
1462 xfree(rest);
1463
1464 return True;
1465}
1466
1467
1468/* not all clibs got ltoa */
1469#define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
1470
1471char *
1472l_to_a(long N, int base)
1473{
1474 static char ret[LTOA_BUFSIZE];
1475
1476 char *head = ret, buf[LTOA_BUFSIZE], *tail = buf + sizeof(buf);
1477
1478 register int divrem;
1479
1480 if (base < 36 || 2 > base)
1481 base = 10;
1482
1483 if (N < 0)
1484 {
1485 *head++ = '-';
1486 N = -N;
1487 }
1488
1489 tail = buf + sizeof(buf);
1490 *--tail = 0;
1491
1492 do
1493 {
1494 divrem = N % base;
1495 *--tail = (divrem <= 9) ? divrem + '0' : divrem + 'a' - 10;
1496 N /= base;
1497 }
1498 while (N);
1499
1500 strcpy(head, tail);
1501 return ret;
1502}
1503
1504
1505int
1506load_licence(unsigned char **data)
1507{
1508 char *home, *path;
1509 struct stat st;
1510 int fd, length;
1511
1512 home = getenv("HOME");
1513 if (home == NULL)
1514 return -1;
1515
1516 path = (char *) xmalloc(strlen(home) + strlen(g_hostname) + sizeof("/.rdesktop/licence."));
1517 sprintf(path, "%s/.rdesktop/licence.%s", home, g_hostname);
1518
1519 fd = open(path, O_RDONLY);
1520 if (fd == -1)
1521 return -1;
1522
1523 if (fstat(fd, &st))
1524 return -1;
1525
1526 *data = (uint8 *) xmalloc(st.st_size);
1527 length = read(fd, *data, st.st_size);
1528 close(fd);
1529 xfree(path);
1530 return length;
1531}
1532
1533void
1534save_licence(unsigned char *data, int length)
1535{
1536 char *home, *path, *tmppath;
1537 int fd;
1538
1539 home = getenv("HOME");
1540 if (home == NULL)
1541 return;
1542
1543 path = (char *) xmalloc(strlen(home) + strlen(g_hostname) + sizeof("/.rdesktop/licence."));
1544
1545 sprintf(path, "%s/.rdesktop", home);
1546 if ((mkdir(path, 0700) == -1) && errno != EEXIST)
1547 {
1548 perror(path);
1549 return;
1550 }
1551
1552 /* write licence to licence.hostname.new, then atomically rename to licence.hostname */
1553
1554 sprintf(path, "%s/.rdesktop/licence.%s", home, g_hostname);
1555 tmppath = (char *) xmalloc(strlen(path) + sizeof(".new"));
1556 strcpy(tmppath, path);
1557 strcat(tmppath, ".new");
1558
1559 fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1560 if (fd == -1)
1561 {
1562 perror(tmppath);
1563 return;
1564 }
1565
1566 if (write(fd, data, length) != length)
1567 {
1568 perror(tmppath);
1569 unlink(tmppath);
1570 }
1571 else if (rename(tmppath, path) == -1)
1572 {
1573 perror(path);
1574 unlink(tmppath);
1575 }
1576
1577 close(fd);
1578 xfree(tmppath);
1579 xfree(path);
1580}
1581
1582/* Create the bitmap cache directory */
1583RD_BOOL
1584rd_pstcache_mkdir(void)
1585{
1586 char *home;
1587 char bmpcache_dir[256];
1588
1589 home = getenv("HOME");
1590
1591 if (home == NULL)
1592 return False;
1593
1594 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop");
1595
1596 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1597 {
1598 perror(bmpcache_dir);
1599 return False;
1600 }
1601
1602 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop/cache");
1603
1604 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1605 {
1606 perror(bmpcache_dir);
1607 return False;
1608 }
1609
1610 return True;
1611}
1612
1613/* open a file in the .rdesktop directory */
1614int
1615rd_open_file(char *filename)
1616{
1617 char *home;
1618 char fn[256];
1619 int fd;
1620
1621 home = getenv("HOME");
1622 if (home == NULL)
1623 return -1;
1624 sprintf(fn, "%s/.rdesktop/%s", home, filename);
1625 fd = open(fn, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1626 if (fd == -1)
1627 perror(fn);
1628 return fd;
1629}
1630
1631/* close file */
1632void
1633rd_close_file(int fd)
1634{
1635 close(fd);
1636}
1637
1638/* read from file*/
1639int
1640rd_read_file(int fd, void *ptr, int len)
1641{
1642 return read(fd, ptr, len);
1643}
1644
1645/* write to file */
1646int
1647rd_write_file(int fd, void *ptr, int len)
1648{
1649 return write(fd, ptr, len);
1650}
1651
1652/* move file pointer */
1653int
1654rd_lseek_file(int fd, int offset)
1655{
1656 return lseek(fd, offset, SEEK_SET);
1657}
1658
1659/* do a write lock on a file */
1660RD_BOOL
1661rd_lock_file(int fd, int start, int len)
1662{
1663 struct flock lock;
1664
1665 lock.l_type = F_WRLCK;
1666 lock.l_whence = SEEK_SET;
1667 lock.l_start = start;
1668 lock.l_len = len;
1669 if (fcntl(fd, F_SETLK, &lock) == -1)
1670 return False;
1671 return True;
1672}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette