VirtualBox

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

Last change on this file since 33656 was 33656, checked in by vboxsync, 14 years ago

*: rebrand Sun (L)GPL disclaimers

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

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