Fixed GetLocaleInfoW to handle Unicode properly and completed
[wine] / scheduler / client.c
1 /*
2  * Client part of the client/server communication
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #ifdef HAVE_PWD_H
29 # include <pwd.h>
30 #endif
31 #include <signal.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #ifdef HAVE_SYS_SOCKET_H
36 # include <sys/socket.h>
37 #endif
38 #ifdef HAVE_SYS_WAIT_H
39 #include <sys/wait.h>
40 #endif
41 #include <sys/un.h>
42 #ifdef HAVE_SYS_MMAN_H
43 #include <sys/mman.h>
44 #endif
45 #include <sys/stat.h>
46 #include <sys/uio.h>
47 #include <unistd.h>
48 #include <stdarg.h>
49
50 #include "thread.h"
51 #include "wine/library.h"
52 #include "wine/server.h"
53 #include "winerror.h"
54 #include "options.h"
55
56 /* Some versions of glibc don't define this */
57 #ifndef SCM_RIGHTS
58 #define SCM_RIGHTS 1
59 #endif
60
61 #define SOCKETNAME "socket"        /* name of the socket file */
62 #define LOCKNAME   "lock"          /* name of the lock file */
63
64 #ifndef HAVE_MSGHDR_ACCRIGHTS
65 /* data structure used to pass an fd with sendmsg/recvmsg */
66 struct cmsg_fd
67 {
68     int len;   /* sizeof structure */
69     int level; /* SOL_SOCKET */
70     int type;  /* SCM_RIGHTS */
71     int fd;    /* fd to pass */
72 };
73 #endif  /* HAVE_MSGHDR_ACCRIGHTS */
74
75 static void *boot_thread_id;
76 static sigset_t block_set;  /* signals to block during server calls */
77 static int fd_socket;  /* socket to exchange file descriptors with the server */
78
79 #ifdef __GNUC__
80 static void fatal_error( const char *err, ... ) __attribute__((noreturn, format(printf,1,2)));
81 static void fatal_perror( const char *err, ... ) __attribute__((noreturn, format(printf,1,2)));
82 static void server_connect_error( const char *serverdir ) __attribute__((noreturn));
83 #endif
84
85 /* die on a fatal error; use only during initialization */
86 static void fatal_error( const char *err, ... )
87 {
88     va_list args;
89
90     va_start( args, err );
91     fprintf( stderr, "wine: " );
92     vfprintf( stderr, err, args );
93     va_end( args );
94     exit(1);
95 }
96
97 /* die on a fatal error; use only during initialization */
98 static void fatal_perror( const char *err, ... )
99 {
100     va_list args;
101
102     va_start( args, err );
103     fprintf( stderr, "wine: " );
104     vfprintf( stderr, err, args );
105     perror( " " );
106     va_end( args );
107     exit(1);
108 }
109
110 /***********************************************************************
111  *           server_protocol_error
112  */
113 void server_protocol_error( const char *err, ... )
114 {
115     va_list args;
116
117     va_start( args, err );
118     fprintf( stderr, "wine client error:%p: ", NtCurrentTeb()->tid );
119     vfprintf( stderr, err, args );
120     va_end( args );
121     SYSDEPS_AbortThread(1);
122 }
123
124
125 /***********************************************************************
126  *           server_protocol_perror
127  */
128 void server_protocol_perror( const char *err )
129 {
130     fprintf( stderr, "wine client error:%p: ", NtCurrentTeb()->tid );
131     perror( err );
132     SYSDEPS_AbortThread(1);
133 }
134
135
136 /***********************************************************************
137  *           send_request
138  *
139  * Send a request to the server.
140  */
141 static void send_request( const struct __server_request_info *req )
142 {
143     int i, ret;
144
145     if (!req->u.req.request_header.request_size)
146     {
147         if ((ret = write( NtCurrentTeb()->request_fd, &req->u.req,
148                           sizeof(req->u.req) )) == sizeof(req->u.req)) return;
149
150     }
151     else
152     {
153         struct iovec vec[__SERVER_MAX_DATA+1];
154
155         vec[0].iov_base = (void *)&req->u.req;
156         vec[0].iov_len = sizeof(req->u.req);
157         for (i = 0; i < req->data_count; i++)
158         {
159             vec[i+1].iov_base = (void *)req->data[i].ptr;
160             vec[i+1].iov_len = req->data[i].size;
161         }
162         if ((ret = writev( NtCurrentTeb()->request_fd, vec, i+1 )) ==
163             req->u.req.request_header.request_size + sizeof(req->u.req)) return;
164     }
165
166     if (ret >= 0) server_protocol_error( "partial write %d\n", ret );
167     if (errno == EPIPE) SYSDEPS_AbortThread(0);
168     server_protocol_perror( "sendmsg" );
169 }
170
171
172 /***********************************************************************
173  *           read_reply_data
174  *
175  * Read data from the reply buffer; helper for wait_reply.
176  */
177 static void read_reply_data( void *buffer, size_t size )
178 {
179     int ret;
180
181     for (;;)
182     {
183         if ((ret = read( NtCurrentTeb()->reply_fd, buffer, size )) > 0)
184         {
185             if (!(size -= ret)) return;
186             buffer = (char *)buffer + ret;
187             continue;
188         }
189         if (!ret) break;
190         if (errno == EINTR) continue;
191         if (errno == EPIPE) break;
192         server_protocol_perror("read");
193     }
194     /* the server closed the connection; time to die... */
195     SYSDEPS_AbortThread(0);
196 }
197
198
199 /***********************************************************************
200  *           wait_reply
201  *
202  * Wait for a reply from the server.
203  */
204 inline static void wait_reply( struct __server_request_info *req )
205 {
206     read_reply_data( &req->u.reply, sizeof(req->u.reply) );
207     if (req->u.reply.reply_header.reply_size)
208         read_reply_data( req->reply_data, req->u.reply.reply_header.reply_size );
209 }
210
211
212 /***********************************************************************
213  *           wine_server_call (NTDLL.@)
214  *
215  * Perform a server call.
216  */
217 unsigned int wine_server_call( void *req_ptr )
218 {
219     struct __server_request_info * const req = req_ptr;
220     sigset_t old_set;
221
222     memset( (char *)&req->u.req + req->size, 0, sizeof(req->u.req) - req->size );
223     sigprocmask( SIG_BLOCK, &block_set, &old_set );
224     send_request( req );
225     wait_reply( req );
226     sigprocmask( SIG_SETMASK, &old_set, NULL );
227     return req->u.reply.reply_header.error;
228 }
229
230
231 /***********************************************************************
232  *           wine_server_send_fd
233  *
234  * Send a file descriptor to the server.
235  */
236 void wine_server_send_fd( int fd )
237 {
238 #ifndef HAVE_MSGHDR_ACCRIGHTS
239     struct cmsg_fd cmsg;
240 #endif
241     struct send_fd data;
242     struct msghdr msghdr;
243     struct iovec vec;
244     int ret;
245
246     vec.iov_base = (void *)&data;
247     vec.iov_len  = sizeof(data);
248
249     msghdr.msg_name    = NULL;
250     msghdr.msg_namelen = 0;
251     msghdr.msg_iov     = &vec;
252     msghdr.msg_iovlen  = 1;
253
254 #ifdef HAVE_MSGHDR_ACCRIGHTS
255     msghdr.msg_accrights    = (void *)&fd;
256     msghdr.msg_accrightslen = sizeof(fd);
257 #else  /* HAVE_MSGHDR_ACCRIGHTS */
258     cmsg.len   = sizeof(cmsg);
259     cmsg.level = SOL_SOCKET;
260     cmsg.type  = SCM_RIGHTS;
261     cmsg.fd    = fd;
262     msghdr.msg_control    = &cmsg;
263     msghdr.msg_controllen = sizeof(cmsg);
264     msghdr.msg_flags      = 0;
265 #endif  /* HAVE_MSGHDR_ACCRIGHTS */
266
267     data.tid = (void *)GetCurrentThreadId();
268     data.fd  = fd;
269
270     for (;;)
271     {
272         if ((ret = sendmsg( fd_socket, &msghdr, 0 )) == sizeof(data)) return;
273         if (ret >= 0) server_protocol_error( "partial write %d\n", ret );
274         if (errno == EINTR) continue;
275         if (errno == EPIPE) SYSDEPS_AbortThread(0);
276         server_protocol_perror( "sendmsg" );
277     }
278 }
279
280
281 /***********************************************************************
282  *           receive_fd
283  *
284  * Receive a file descriptor passed from the server.
285  */
286 static int receive_fd( obj_handle_t *handle )
287 {
288     struct iovec vec;
289     int ret, fd;
290
291 #ifdef HAVE_MSGHDR_ACCRIGHTS
292     struct msghdr msghdr;
293
294     fd = -1;
295     msghdr.msg_accrights    = (void *)&fd;
296     msghdr.msg_accrightslen = sizeof(fd);
297 #else  /* HAVE_MSGHDR_ACCRIGHTS */
298     struct msghdr msghdr;
299     struct cmsg_fd cmsg;
300
301     cmsg.len   = sizeof(cmsg);
302     cmsg.level = SOL_SOCKET;
303     cmsg.type  = SCM_RIGHTS;
304     cmsg.fd    = -1;
305     msghdr.msg_control    = &cmsg;
306     msghdr.msg_controllen = sizeof(cmsg);
307     msghdr.msg_flags      = 0;
308 #endif  /* HAVE_MSGHDR_ACCRIGHTS */
309
310     msghdr.msg_name    = NULL;
311     msghdr.msg_namelen = 0;
312     msghdr.msg_iov     = &vec;
313     msghdr.msg_iovlen  = 1;
314     vec.iov_base = (void *)handle;
315     vec.iov_len  = sizeof(*handle);
316
317     for (;;)
318     {
319         if ((ret = recvmsg( fd_socket, &msghdr, 0 )) > 0)
320         {
321 #ifndef HAVE_MSGHDR_ACCRIGHTS
322             fd = cmsg.fd;
323 #endif
324             if (fd == -1) server_protocol_error( "no fd received for handle %d\n", *handle );
325             fcntl( fd, F_SETFD, 1 ); /* set close on exec flag */
326             return fd;
327         }
328         if (!ret) break;
329         if (errno == EINTR) continue;
330         if (errno == EPIPE) break;
331         server_protocol_perror("recvmsg");
332     }
333     /* the server closed the connection; time to die... */
334     SYSDEPS_AbortThread(0);
335 }
336
337
338 /***********************************************************************
339  *           store_cached_fd
340  *
341  * Store the cached fd value for a given handle back into the server.
342  * Returns the new fd, which can be different if there was already an
343  * fd in the cache for that handle.
344  */
345 inline static int store_cached_fd( int fd, obj_handle_t handle )
346 {
347     SERVER_START_REQ( set_handle_info )
348     {
349         req->handle = handle;
350         req->flags  = 0;
351         req->mask   = 0;
352         req->fd     = fd;
353         if (!wine_server_call( req ))
354         {
355             if (reply->cur_fd != fd)
356             {
357                 /* someone was here before us */
358                 close( fd );
359                 fd = reply->cur_fd;
360             }
361         }
362         else
363         {
364             close( fd );
365             fd = -1;
366         }
367     }
368     SERVER_END_REQ;
369     return fd;
370 }
371
372
373 /***********************************************************************
374  *           wine_server_fd_to_handle   (NTDLL.@)
375  *
376  * Allocate a file handle for a Unix fd.
377  */
378 int wine_server_fd_to_handle( int fd, unsigned int access, int inherit, obj_handle_t *handle )
379 {
380     int ret;
381
382     *handle = 0;
383     wine_server_send_fd( fd );
384
385     SERVER_START_REQ( alloc_file_handle )
386     {
387         req->access  = access;
388         req->inherit = inherit;
389         req->fd      = fd;
390         if (!(ret = wine_server_call( req ))) *handle = reply->handle;
391     }
392     SERVER_END_REQ;
393     return ret;
394 }
395
396
397 /***********************************************************************
398  *           wine_server_handle_to_fd   (NTDLL.@)
399  *
400  * Retrieve the Unix fd corresponding to a file handle.
401  */
402 int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *unix_fd,
403                               enum fd_type *type, int *flags )
404 {
405     obj_handle_t fd_handle;
406     int ret, fd = -1;
407
408     *unix_fd = -1;
409     for (;;)
410     {
411         SERVER_START_REQ( get_handle_fd )
412         {
413             req->handle = handle;
414             req->access = access;
415             if (!(ret = wine_server_call( req ))) fd = reply->fd;
416             if (type) *type = reply->type;
417             if (flags) *flags = reply->flags;
418         }
419         SERVER_END_REQ;
420         if (ret) return ret;
421
422         if (fd != -1) break;
423
424         /* it wasn't in the cache, get it from the server */
425         fd = receive_fd( &fd_handle );
426         /* and store it back into the cache */
427         fd = store_cached_fd( fd, fd_handle );
428
429         if (fd_handle == handle) break;
430         /* if we received a different handle this means there was
431          * a race with another thread; we restart everything from
432          * scratch in this case.
433          */
434     }
435
436     if ((fd != -1) && ((fd = dup(fd)) == -1)) return STATUS_TOO_MANY_OPENED_FILES;
437     *unix_fd = fd;
438     return STATUS_SUCCESS;
439 }
440
441
442 /***********************************************************************
443  *           start_server
444  *
445  * Start a new wine server.
446  */
447 static void start_server( const char *oldcwd )
448 {
449     static int started;  /* we only try once */
450     char *path, *p;
451     if (!started)
452     {
453         int status;
454         int pid = fork();
455         if (pid == -1) fatal_perror( "fork" );
456         if (!pid)
457         {
458             /* if server is explicitly specified, use this */
459             if ((p = getenv("WINESERVER")))
460             {
461                 if (p[0] != '/' && oldcwd[0] == '/')  /* make it an absolute path */
462                 {
463                     if (!(path = malloc( strlen(oldcwd) + strlen(p) + 1 )))
464                         fatal_error( "out of memory\n" );
465                     sprintf( path, "%s/%s", oldcwd, p );
466                     p = path;
467                 }
468                 execl( p, p, NULL );
469                 fatal_perror( "could not exec the server '%s'\n"
470                               "    specified in the WINESERVER environment variable", p );
471             }
472
473             /* first try the installation dir */
474             execl( BINDIR "/wineserver", "wineserver", NULL );
475
476             /* now try the dir we were launched from */
477             if (full_argv0)
478             {
479                 if (!(path = malloc( strlen(full_argv0) + 20 )))
480                     fatal_error( "out of memory\n" );
481                 if ((p = strrchr( strcpy( path, full_argv0 ), '/' )))
482                 {
483                     strcpy( p, "/wineserver" );
484                     execl( path, path, NULL );
485                 }
486                 free(path);
487             }
488
489             /* finally try the path */
490             execlp( "wineserver", "wineserver", NULL );
491             fatal_error( "could not exec wineserver\n" );
492         }
493         waitpid( pid, &status, 0 );
494         status = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
495         if (status == 2) return;  /* server lock held by someone else, will retry later */
496         if (status) exit(status);  /* server failed */
497         started = 1;
498     }
499 }
500
501
502 /***********************************************************************
503  *           server_connect_error
504  *
505  * Try to display a meaningful explanation of why we couldn't connect
506  * to the server.
507  */
508 static void server_connect_error( const char *serverdir )
509 {
510     int fd;
511     struct flock fl;
512
513     if ((fd = open( LOCKNAME, O_WRONLY )) == -1)
514         fatal_error( "for some mysterious reason, the wine server never started.\n" );
515
516     fl.l_type   = F_WRLCK;
517     fl.l_whence = SEEK_SET;
518     fl.l_start  = 0;
519     fl.l_len    = 1;
520     if (fcntl( fd, F_GETLK, &fl ) != -1)
521     {
522         if (fl.l_type == F_WRLCK)  /* the file is locked */
523             fatal_error( "a wine server seems to be running, but I cannot connect to it.\n"
524                          "   You probably need to kill that process (it might be pid %d).\n",
525                          (int)fl.l_pid );
526         fatal_error( "for some mysterious reason, the wine server failed to run.\n" );
527     }
528     fatal_error( "the file system of '%s' doesn't support locks,\n"
529           "   and there is a 'socket' file in that directory that prevents wine from starting.\n"
530           "   You should make sure no wine server is running, remove that file and try again.\n",
531                  serverdir );
532 }
533
534
535 /***********************************************************************
536  *           server_connect
537  *
538  * Attempt to connect to an existing server socket.
539  * We need to be in the server directory already.
540  */
541 static int server_connect( const char *oldcwd, const char *serverdir )
542 {
543     struct sockaddr_un addr;
544     struct stat st;
545     int s, slen, retry;
546
547     /* chdir to the server directory */
548     if (chdir( serverdir ) == -1)
549     {
550         if (errno != ENOENT) fatal_perror( "chdir to %s", serverdir );
551         start_server( "." );
552         if (chdir( serverdir ) == -1) fatal_perror( "chdir to %s", serverdir );
553     }
554
555     /* make sure we are at the right place */
556     if (stat( ".", &st ) == -1) fatal_perror( "stat %s", serverdir );
557     if (st.st_uid != getuid()) fatal_error( "'%s' is not owned by you\n", serverdir );
558     if (st.st_mode & 077) fatal_error( "'%s' must not be accessible by other users\n", serverdir );
559
560     for (retry = 0; retry < 6; retry++)
561     {
562         /* if not the first try, wait a bit to leave the previous server time to exit */
563         if (retry)
564         {
565             usleep( 100000 * retry * retry );
566             start_server( oldcwd );
567             if (lstat( SOCKETNAME, &st ) == -1) continue;  /* still no socket, wait a bit more */
568         }
569         else if (lstat( SOCKETNAME, &st ) == -1) /* check for an already existing socket */
570         {
571             if (errno != ENOENT) fatal_perror( "lstat %s/%s", serverdir, SOCKETNAME );
572             start_server( oldcwd );
573             if (lstat( SOCKETNAME, &st ) == -1) continue;  /* still no socket, wait a bit more */
574         }
575
576         /* make sure the socket is sane (ISFIFO needed for Solaris) */
577         if (!S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode))
578             fatal_error( "'%s/%s' is not a socket\n", serverdir, SOCKETNAME );
579         if (st.st_uid != getuid())
580             fatal_error( "'%s/%s' is not owned by you\n", serverdir, SOCKETNAME );
581
582         /* try to connect to it */
583         addr.sun_family = AF_UNIX;
584         strcpy( addr.sun_path, SOCKETNAME );
585         slen = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path) + 1;
586 #ifdef HAVE_SOCKADDR_SUN_LEN
587         addr.sun_len = slen;
588 #endif
589         if ((s = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );
590         if (connect( s, (struct sockaddr *)&addr, slen ) != -1)
591         {
592             fcntl( s, F_SETFD, 1 ); /* set close on exec flag */
593             return s;
594         }
595         close( s );
596     }
597     server_connect_error( serverdir );
598 }
599
600
601 /***********************************************************************
602  *           CLIENT_InitServer
603  *
604  * Start the server and create the initial socket pair.
605  */
606 void CLIENT_InitServer(void)
607 {
608     int size;
609     char *oldcwd;
610     obj_handle_t dummy_handle;
611
612     /* retrieve the current directory */
613     for (size = 512; ; size *= 2)
614     {
615         if (!(oldcwd = malloc( size ))) break;
616         if (getcwd( oldcwd, size )) break;
617         free( oldcwd );
618         if (errno == ERANGE) continue;
619         oldcwd = NULL;
620         break;
621     }
622
623     /* if argv[0] is a relative path, make it absolute */
624     full_argv0 = argv0;
625     if (oldcwd && argv0[0] != '/' && strchr( argv0, '/' ))
626     {
627         char *new_argv0 = malloc( strlen(oldcwd) + strlen(argv0) + 2 );
628         if (new_argv0)
629         {
630             strcpy( new_argv0, oldcwd );
631             strcat( new_argv0, "/" );
632             strcat( new_argv0, argv0 );
633             full_argv0 = new_argv0;
634         }
635     }
636
637     /* connect to the server */
638     fd_socket = server_connect( oldcwd, wine_get_server_dir() );
639
640     /* switch back to the starting directory */
641     if (oldcwd)
642     {
643         chdir( oldcwd );
644         free( oldcwd );
645     }
646
647     /* setup the signal mask */
648     sigemptyset( &block_set );
649     sigaddset( &block_set, SIGALRM );
650     sigaddset( &block_set, SIGIO );
651     sigaddset( &block_set, SIGINT );
652     sigaddset( &block_set, SIGHUP );
653
654     /* receive the first thread request fd on the main socket */
655     NtCurrentTeb()->request_fd = receive_fd( &dummy_handle );
656
657     CLIENT_InitThread();
658 }
659
660
661 /***********************************************************************
662  *           CLIENT_InitThread
663  *
664  * Send an init thread request. Return 0 if OK.
665  */
666 void CLIENT_InitThread(void)
667 {
668     TEB *teb = NtCurrentTeb();
669     int version, ret;
670     int reply_pipe[2];
671
672     /* ignore SIGPIPE so that we get a EPIPE error instead  */
673     signal( SIGPIPE, SIG_IGN );
674     /* automatic child reaping to avoid zombies */
675     signal( SIGCHLD, SIG_IGN );
676
677     /* create the server->client communication pipes */
678     if (pipe( reply_pipe ) == -1) server_protocol_perror( "pipe" );
679     if (pipe( teb->wait_fd ) == -1) server_protocol_perror( "pipe" );
680     wine_server_send_fd( reply_pipe[1] );
681     wine_server_send_fd( teb->wait_fd[1] );
682     teb->reply_fd = reply_pipe[0];
683     close( reply_pipe[1] );
684
685     /* set close on exec flag */
686     fcntl( teb->reply_fd, F_SETFD, 1 );
687     fcntl( teb->wait_fd[0], F_SETFD, 1 );
688     fcntl( teb->wait_fd[1], F_SETFD, 1 );
689
690     SERVER_START_REQ( init_thread )
691     {
692         req->unix_pid    = getpid();
693         req->teb         = teb;
694         req->entry       = teb->entry_point;
695         req->reply_fd    = reply_pipe[1];
696         req->wait_fd     = teb->wait_fd[1];
697         ret = wine_server_call( req );
698         teb->pid = reply->pid;
699         teb->tid = reply->tid;
700         version  = reply->version;
701         if (reply->boot) boot_thread_id = teb->tid;
702         else if (boot_thread_id == teb->tid) boot_thread_id = 0;
703     }
704     SERVER_END_REQ;
705
706     if (ret) server_protocol_error( "init_thread failed with status %x\n", ret );
707     if (version != SERVER_PROTOCOL_VERSION)
708         server_protocol_error( "version mismatch %d/%d.\n"
709                                "Your %s binary was not upgraded correctly,\n"
710                                "or you have an older one somewhere in your PATH.\n"
711                                "Or maybe the wrong wineserver is still running?\n",
712                                version, SERVER_PROTOCOL_VERSION,
713                                (version > SERVER_PROTOCOL_VERSION) ? "wine" : "wineserver" );
714 }
715
716
717 /***********************************************************************
718  *           CLIENT_BootDone
719  *
720  * Signal that we have finished booting, and set debug level.
721  */
722 void CLIENT_BootDone( int debug_level )
723 {
724     SERVER_START_REQ( boot_done )
725     {
726         req->debug_level = debug_level;
727         wine_server_call( req );
728     }
729     SERVER_END_REQ;
730 }
731
732
733 /***********************************************************************
734  *           CLIENT_IsBootThread
735  *
736  * Return TRUE if current thread is the boot thread.
737  */
738 int CLIENT_IsBootThread(void)
739 {
740     return (GetCurrentThreadId() == (DWORD)boot_thread_id);
741 }