modules/sk/sk_socket.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- SK_atoport
- func_sigusr
- SK_close
- SK_getsock
- SK_accept_connection
- SK_read
- SK_write
- SK_gets
- SK_puts
- SK_putc
- SK_getc
- SK_getpeername
- SK_getpeerip
- SK_cd_puts
- SK_cd_gets
- SK_cd_close
- SK_cd_printf
- sk_watchdog
- SK_watchstart
- SK_watchstop
- SK_watchkill
- SK_watchexec
- SK_watchclear
1 /***************************************
2 $Revision: 1.3 $
3
4 Example code: A socket module.
5
6 Status: NOT REVUED, NOT TESTED
7
8 +html+ <DL COMPACT>
9 +html+ <DT>Online References:
10 +html+ <DD><UL>
11 +html+ <LI>Adapted from <A HREF="http://www.ibrado.com/sock-faq/sfaq.html#faq65">sample source code</A>.
12 +html+ </UL>
13 +html+ </DL>
14 +html+ <PRE>
15 +html+ </PRE>
16
17 ******************/ /******************
18 Modification History:
19 ottrey (08/03/1999) Created from sockhelp.c.
20 ottrey (08/03/1998) Heavily butchered.
21 joao (22/06/1999) Modified socket creation and accepts.
22 ******************/ /******************
23 REMINDER: PUT THE PROPER COPYRIGHT NOTICE HERE
24 ***************************************/
25 #include <arpa/inet.h>
26 #include "socket.h"
27 #include "constants.h"
28 #include "stubs.h"
29
30 #include "iproutines.h"
31 #include "memwrap.h"
32
33 #include <pthread.h>
34
35 extern int h_errno;
36
37
38 /*+ String sizes +*/
39 #define STR_S 63
40 #define STR_M 255
41 #define STR_L 1023
42 #define STR_XL 4095
43 #define STR_XXL 16383
44
45 /* SK_atoport() */
46 /*++++++++++++++++++++++++++++++++++++++
47 Take a service name, and a service type, and return a port number. If the
48 service name is not found, it tries it as a decimal number. The number
49 returned is byte ordered for the network.
50
51 char *service Service name (or port number).
52
53 char *proto Protocol (eg "tcp").
54
55 More:
56 +html+ <PRE>
57 Authors:
58 ottrey
59
60 +html+ </PRE><DL COMPACT>
61 +html+ <DT>Online References:
62 +html+ <DD><UL>
63 +html+ </UL></DL>
64
65 ++++++++++++++++++++++++++++++++++++++*/
66 int SK_atoport(const char *service, const char *proto) {
/* [<][>][^][v][top][bottom][index][help] */
67 int port;
68 long int lport;
69 struct servent *serv;
70 char *errpos;
71 struct servent result;
72 char buffer[STR_XXL];
73
74 /* First try to read it from /etc/services */
75
76 /* serv = getservbyname(service, proto); */
77 serv = getservbyname_r(service, proto, &result, buffer, sizeof(buffer));
78 if (serv != NULL)
79 port = serv->s_port;
80 else { /* Not in services, maybe a number? */
81 lport = strtol(service,&errpos,0);
82 if ( (errpos[0] != 0) || (lport < 1) || (lport > 65535) )
83 return -1; /* Invalid port address */
84 port = htons(lport);
85 }
86 return port;
87 } /* SK_atoport() */
88
89
90 /* SK_close_listening_socket() */
91 /*++++++++++++++++++++++++++++++++++++++
92 XXX Note: Not sure how long this function will last. Shouldn't _really_ need it.
93
94 More:
95 +html+ <PRE>
96 Authors:
97 ottrey
98
99 +html+ </PRE><DL COMPACT>
100 +html+ <DT>Online References:
101 +html+ <DD><UL>
102 +html+ </UL></DL>
103
104 ++++++++++++++++++++++++++++++++++++++*/
105 /*void SK_close_listening_socket() {
106 close(listening_socket);
107 } */ /* SK_close_listening_socket */
108
109 static void func_sigusr(int n) {
/* [<][>][^][v][top][bottom][index][help] */
110 ER_dbg_va(FAC_SK, ASP_SK_GEN,"func_sigusr(%d) called", n);
111 }
112
113 void SK_close(int socket) {
/* [<][>][^][v][top][bottom][index][help] */
114 ER_dbg_va(FAC_SK, ASP_SK_GEN, "Closing socket... %d", socket);
115
116 close(socket);
117 }
118
119 /* SK_getsock() */
120 /*++++++++++++++++++++++++++++++++++++++
121
122 This function creates a socket and binds to it
123
124 int SK_getsock The new socket
125
126 int socket_type SOCK_STREAM or SOCK_DGRAM (TCP or UDP sockets)
127
128 u_short port The port to listen on. Remember that ports < 1024 are
129 reserved for the root user. Must be passed in network byte
130 order (see "man htons").
131
132 uint32_t bind_address Address to bind to, in network order.
133 More:
134 +html+ <PRE>
135 Authors:
136 ottrey
137 joao
138
139 +html+ </PRE><DL COMPACT>
140 +html+ <DT>Online References:
141 +html+ <DD><UL>
142 +html+ </UL></DL>
143
144 ++++++++++++++++++++++++++++++++++++++*/
145 int SK_getsock(int socket_type, u_short port, uint32_t bind_address) {
/* [<][>][^][v][top][bottom][index][help] */
146 struct sockaddr_in address;
147 int listening_socket;
148 int reuse_addr = 1;
149
150 /* Setup internet address information.
151 This is used with the bind() call */
152 memset((char *) &address, 0, sizeof(address));
153 address.sin_family = AF_INET;
154 address.sin_port = port;
155 address.sin_addr.s_addr = bind_address;
156
157 /* Map all of the signals and exit routine */
158
159 listening_socket = socket(AF_INET, socket_type, 0);
160 if (listening_socket < 0) {
161 perror("socket");
162 exit(EXIT_FAILURE);
163 }
164
165 setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse_addr, sizeof(reuse_addr));
166
167 if (bind(listening_socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
168 perror("bind");
169 close(listening_socket);
170 exit(EXIT_FAILURE);
171 }
172
173
174 if (socket_type == SOCK_STREAM) {
175 listen(listening_socket, 5); /* Queue up to five connections before
176 having them automatically rejected. */
177 }
178
179 return listening_socket;
180 } /* SK_getsock() */
181
182 /*++++++++++++++++++++++++++++++++++++++
183
184 Wait for an incoming connection on the specified socket
185
186 int SK_accept_connection The socket for communicating to the client
187
188 int listening_socket The socket that the server is bound to
189
190 More:
191 +html+ <PRE>
192 Authors:
193 joao
194 +html+ </PRE>
195 ++++++++++++++++++++++++++++++++++++++*/
196 int SK_accept_connection(int listening_socket) {
/* [<][>][^][v][top][bottom][index][help] */
197 int connected_socket = -1;
198
199 while(connected_socket < 0) {
200
201 ER_dbg_va(FAC_SK, ASP_SK_GEN,
202 "Going to accept connections on socket : %d",listening_socket);
203
204 /* XXX joao - ? - why is this here?
205 fflush(NULL);
206 */
207
208 connected_socket = accept(listening_socket, NULL, NULL);
209 if (connected_socket < 0) {
210 /* Either a real error occured, or blocking was interrupted for
211 some reason. Only abort execution if a real error occured. */
212 if (errno != EINTR) {
213 perror("accept");
214 close(listening_socket);
215 return(-1);
216 /* no exit, just return with error */
217 } else {
218 continue; /* don't return - do the accept again */
219 }
220 }
221 }
222
223 ER_dbg_va(FAC_SK, ASP_SK_GEN, "client connected on socket %d",
224 connected_socket
225 );
226
227 return connected_socket;
228 }
229
230 /* SK_read() */
231 /*++++++++++++++++++++++++++++++++++++++
232
233 This is just like the read() system call, except that it will make
234 sure that all your data goes through the socket.
235
236 int SK_read The number of bytes read.
237
238 int sockfd The socket file descriptor.
239
240 char *buf The buffer to be read from the socket.
241
242 size_t count The number of bytes in the buffer.
243
244 More:
245 +html+ <PRE>
246 Authors:
247 ottrey
248 +html+ </PRE>
249 ++++++++++++++++++++++++++++++++++++++*/
250 int SK_read(int sockfd, char *buf, size_t count) {
/* [<][>][^][v][top][bottom][index][help] */
251 size_t bytes_read = 0;
252 int this_read;
253
254 while (bytes_read < count) {
255 do
256 this_read = read(sockfd, buf, count - bytes_read);
257 while ( (this_read < 0) && (errno == EINTR) );
258 if (this_read < 0)
259 return this_read;
260 else if (this_read == 0)
261 return bytes_read;
262 bytes_read += this_read;
263 buf += this_read;
264 }
265
266 return count;
267
268 } /* SK_read() */
269
270
271 /* SK_write() */
272 /*++++++++++++++++++++++++++++++++++++++
273
274 This is just like the write() system call, accept that it will
275 make sure that all data is transmitted.
276
277 int sockfd The socket file descriptor.
278
279 char *buf The buffer to be written to the socket.
280
281 size_t count The number of bytes in the buffer.
282
283 More:
284 +html+ <PRE>
285 Authors:
286 ottrey
287
288 +html+ </PRE><DL COMPACT>
289 +html+ <DT>Online References:
290 +html+ <DD><UL>
291 +html+ </UL></DL>
292
293 ++++++++++++++++++++++++++++++++++++++*/
294 int SK_write(int sockfd, const char *buf, size_t count) {
/* [<][>][^][v][top][bottom][index][help] */
295 size_t bytes_sent = 0;
296 int this_write;
297
298
299 ER_dbg_va(FAC_SK, ASP_SK_WRIT,
300 "SK_write = { sockfd=[%d], buf=[%s], count=[%d]",
301 sockfd, buf, count);
302
303 while (bytes_sent < count) {
304 do
305 this_write = write(sockfd, buf, count - bytes_sent);
306 while ( (this_write < 0) && (errno == EINTR) );
307 if (this_write <= 0)
308 return this_write;
309 bytes_sent += this_write;
310 buf += this_write;
311 }
312 return count;
313 } /* SK_write() */
314
315
316 /* SK_gets() */
317 /*++++++++++++++++++++++++++++++++++++++
318
319 This function reads from a socket, until it recieves a linefeed
320 character. It fills the buffer "str" up to the maximum size "count".
321
322 int SK_gets The total_count of bytes read.
323
324 int sockfd The socket file descriptor.
325
326 char *str The buffer to be written from the socket.
327
328 size_t count The number of bytes in the buffer.
329
330 More:
331 +html+ <PRE>
332 Authors:
333 ottrey
334
335 Side Effects:
336 This function will return -1 if the socket is closed during the read operation.
337
338 Note that if a single line exceeds the length of count, the extra data
339 will be read and discarded! You have been warned.
340
341 To Do:
342 Capture the control-c properly!
343
344 +html+ </PRE>
345
346 ++++++++++++++++++++++++++++++++++++++*/
347 int SK_gets(int sockfd, char *str, size_t count) {
/* [<][>][^][v][top][bottom][index][help] */
348 int bytes_read;
349 int total_count = 0;
350 char *current_position;
351 char last_read = 0;
352
353 int control_c = 0;
354
355 current_position = str;
356 while (last_read != 10) {
357
358
359
360 bytes_read = read(sockfd, &last_read, 1);
361 if (bytes_read <= 0) {
362 /* The other side may have closed unexpectedly */
363 return SK_DISCONNECT;
364 /* Is this effective on other platforms than linux? */
365 }
366 if ( (total_count < count) && (last_read != 10) && (last_read !=13) ) {
367 *current_position = last_read;
368 current_position++;
369 total_count++;
370 }
371
372 if (last_read == -1) {
373 bytes_read = read(sockfd, &last_read, 1);
374 if (last_read == -12) {
375 ER_dbg_va(FAC_SK, ASP_SK_GEN,"Client pressed Control-c");
376 control_c = 1;
377 ER_dbg_va(FAC_SK, ASP_SK_GEN,"returning SK_INTERRUPT");
378 return SK_INTERRUPT;
379 }
380 }
381 }
382 if (count > 0) {
383 *current_position = 0;
384 }
385
386 return total_count;
387
388 } /* SK_gets() */
389
390
391 /* SK_puts() */
392 /*++++++++++++++++++++++++++++++++++++++
393
394 This function writes a character string out to a socket.
395
396 int SK_puts The total_count of bytes written,
397 or errors (represented as negative numbers)
398
399 int sockfd The socket file descriptor.
400
401 char *str The buffer to be written from the socket.
402
403 More:
404 +html+ <PRE>
405 Authors:
406 ottrey
407
408 Side Effects:
409 This function will return -1 if the socket is closed during the write operation.
410
411 Note that if a single line exceeds the length of count, the extra data
412 will be read and discarded! You have been warned.
413
414 +html+ </PRE>
415
416 ++++++++++++++++++++++++++++++++++++++*/
417 int SK_puts(int sockfd, const char *str) {
/* [<][>][^][v][top][bottom][index][help] */
418
419 return SK_write(sockfd, str, strlen(str));
420
421 } /* SK_puts() */
422
423 /* SK_putc() */
424 /*++++++++++++++++++++++++++++++++++++++
425
426 int SK_putc This function writes a single character out to a socket.
427
428 int sockfd socket
429 char ch character
430
431 return number of chars written
432
433 ++++++++++++++++++++++++++++++++++++++*/
434 int SK_putc(int sockfd, char ch) {
/* [<][>][^][v][top][bottom][index][help] */
435 return SK_write(sockfd, &ch, 1);
436 }/* SK_putc() */
437
438 /*++++++++++++++++++++++++++++++++++++++
439
440 This function reads a single character from a socket.
441
442 returns EOF when no character can be read.
443
444 ++++++++++++++++++++++++++++++++++++++*/
445 int SK_getc(int sockfd) {
/* [<][>][^][v][top][bottom][index][help] */
446 char ch;
447
448 if( read(sockfd, &ch, 1) <= 0 ) {
449 return EOF;
450 }
451 else {
452 return ch;
453 }
454 }/* SK_getc() */
455
456 /* SK_getpeername() */
457 /*++++++++++++++++++++++++++++++++++++++
458
459 This function will tell you who is at the other end of a connected stream socket.
460 XXX It's not working.
461 XXX ? MB it is...
462
463 int sockfd The socket file descriptor.
464
465 More:
466 +html+ <PRE>
467 Authors:
468 ottrey
469 +html+ </PRE>
470
471 ++++++++++++++++++++++++++++++++++++++*/
472 char *SK_getpeername(int sockfd)
/* [<][>][^][v][top][bottom][index][help] */
473 {
474 char *hostaddress=NULL;
475 struct sockaddr_in addr_in;
476 int namelen=sizeof(addr_in);
477
478 if (getpeername(sockfd, (struct sockaddr *)&addr_in, &namelen) != -1) {
479
480 dieif( wr_malloc((void **)&hostaddress, 16) != UT_OK);
481
482 strcpy(hostaddress, inet_ntoa(addr_in.sin_addr)); /* XXX MT-UNSAFE */
483 }
484
485 return hostaddress;
486
487 } /* SK_getpeername() */
488
489 /* SK_getpeerip */
490 int SK_getpeerip(int sockfd, ip_addr_t *ip) {
/* [<][>][^][v][top][bottom][index][help] */
491 struct sockaddr_in addr_in;
492 int namelen=sizeof(addr_in);
493 int ret=-1;
494
495 memset(& addr_in, 0, sizeof(struct sockaddr_in));
496
497 if (getpeername(sockfd, (struct sockaddr *)(& addr_in), &namelen) != -1) {
498 ret=0;
499 IP_addr_s2b(ip, &addr_in, namelen);
500 }
501
502 return ret;
503 }
504
505 /*-------------------------------------------------------------------
506 * CD varieties of the functions: broken connections get registered
507 * in the connection structure within the query environment
508 * as side effects.
509 * -----------------------------------------------------------------*/
510
511 /* SK_cd_puts() */
512 /*++++++++++++++++++++++++++++++++++++++
513
514 This function writes a character string out to a socket.
515
516 int SK_qe_puts The total_count of bytes written,
517 or errors (represented as negative numbers)
518
519 sk_conn_st *condat connection data
520
521 char *str The buffer to be written from the socket.
522
523 More:
524 if the connection structure has bad status for this connection
525 from previous calls, no write will be attempted.
526
527 +html+ <PRE>
528 Authors:
529 marek
530
531 Side Effects:
532 broken connections get registered
533 in the connection structure within the query environment
534
535 +html+ </PRE>
536
537 ++++++++++++++++++++++++++++++++++++++*/
538 int SK_cd_puts(sk_conn_st *condat, const char *str) {
/* [<][>][^][v][top][bottom][index][help] */
539 int res=SK_puts(condat->sock, str);
540
541 if( res < 0 ){
542 /* set the corresponding rtc flag */
543 condat->rtc |= (-res);
544
545 switch( - res ) {
546 /* dont know what to do and how to log */
547 case SK_DISCONNECT:
548 case SK_INTERRUPT:
549 /*("Thread received a control-c\n");*/
550 case SK_TIMEOUT:
551 /*("Reading timed out\n");*/
552 break;
553 default:
554 /* unexpected error code. bail out */
555 die;
556 }
557 }
558 } /* SK_cd_puts() */
559
560 /* SK_cd_gets() */
561 /*++++++++++++++++++++++++++++++++++++++
562
563 Wrapper around SK_gets.
564
565 int SK_cd_gets The total_count of bytes read,
566 or errors (represented as negative numbers)
567
568 sk_conn_st *condat connection data
569
570 char *str The buffer to be written from the socket.
571
572 More:
573 if the connection structure has bad status for this connection
574 from previous calls, no write will be attempted.
575
576 +html+ <PRE>
577 Authors:
578 marek
579
580 Side Effects:
581 broken connections get registered
582 in the connection structure within the query environment
583
584 +html+ </PRE>
585
586 ++++++++++++++++++++++++++++++++++++++*/
587 int SK_cd_gets(sk_conn_st *condat, char *str, size_t count) {
/* [<][>][^][v][top][bottom][index][help] */
588 int res;
589 fd_set rset;
590 struct timeval *ptm = & condat->rd_timeout;
591 int readcount = 0;
592
593 memset( str, 0, count);
594 FD_ZERO( &rset );
595 FD_SET( condat->sock, &rset );
596
597 if( ptm->tv_sec == 0 && ptm->tv_usec == 0) { /* if timeout undefined,
598 do blocking I/O */
599 ptm = NULL;
600 }
601
602 do {
603 char buf[2];
604 int sel = select( (condat->sock)+1, &rset, NULL, NULL, ptm);
605
606 dieif(sel < 0); /* we don't expect problems */
607
608 if( sel == 0 ) {
609 condat->rtc |= SK_TIMEOUT;
610 break;
611 }
612
613 else {
614 read( condat->sock, buf, 1 );
615 str[readcount] = buf[0];
616 readcount++;
617 if( buf[0] == '\n' ) {
618 break;
619 }
620 }
621 } while( readcount < count );
622
623 } /* SK_cd_gets() */
624
625
626 int SK_cd_close(sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
627 SK_close(condat->sock);
628 } /* SK_cd_close() */
629
630
631 /* print to condat like printf
632
633 by marek
634 */
635 int SK_cd_printf(sk_conn_st *condat, char *txt, ...)
/* [<][>][^][v][top][bottom][index][help] */
636 {
637 #define SKBUFLEN 2047
638 va_list ap;
639 char buffer[SKBUFLEN+1];
640 int len;
641 char *newbuf = NULL;
642 char *finalbuf = buffer; /* points to where the text REALLY is */
643
644 /* vsnprintf returns the number of character it WOULD write if it could.
645 So we assume the buffer to be of adequate size for most cases,
646 and if it isn't, then we allocate to newbuf and call v*printf again
647 */
648 va_start(ap, txt);
649 len = vsnprintf(buffer, SKBUFLEN, txt, ap);
650 va_end(ap);
651
652 if( len > SKBUFLEN ) {
653 dieif(!NOERR(wr_malloc( (void **)& newbuf, len+1)));
654
655 va_start(ap, txt);
656 vsnprintf(newbuf, len, txt, ap);
657 va_end(ap);
658
659 finalbuf = newbuf;
660 }
661 /* terminate */
662 finalbuf[len] = 0;
663
664 /* reuse len */
665 len = SK_cd_puts(condat, finalbuf);
666
667 if(newbuf != NULL) {
668 wr_free(newbuf);
669 }
670
671 return len;
672 }
673
674
675 /* sk_watchdog - started as a separate thread.
676
677 selects on the given socket; discards all input.
678 whenever it sees end of file (socket closed), it
679 * sets a corresponding flag in the condat structure,
680 * kills a thread designated to be killed (by SK_watchkill)
681
682 by marek;
683 */
684 static
685 void *sk_watchdog(void *arg)
/* [<][>][^][v][top][bottom][index][help] */
686 {
687 sk_conn_st *condat = (sk_conn_st *) arg;
688 int nready;
689 int n;
690 fd_set rset;
691 char buff[STR_S];
692 int socket = condat->sock;
693 sigset_t sset;
694 struct sigaction act;
695
696 FD_ZERO(&rset);
697 FD_SET(socket, &rset);
698
699 sigemptyset(&sset);
700 sigaddset(&sset, SIGUSR1);
701
702 act.sa_handler = func_sigusr;
703 act.sa_flags = 0;
704 dieif(sigaction(SIGUSR1, &act, NULL) != 0);
705
706 dieif(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) != 0);
707
708 pthread_mutex_unlock( & condat->watchmutex ); /* now ready for signal */
709
710 while ((nready=select(socket+1, &rset, NULL, NULL, NULL))!=-1) {
711
712 /* don't even try to read if we have been killed */
713 if( errno == EINTR ) {
714 break;
715 }
716
717 /* There was some input or client half of connection was closed */
718 /* Check for the latter */
719 if (( n=read(socket, buff, sizeof(buff))) == 0) {
720 /* Connection was closed by client */
721 /* Now send a cancellation request to the whois thread. */
722 /* mysql thread will be terminated by thread cleanup routine */
723
724 /* set the reason-to-close flag on this connection */
725 condat->rtc |= SK_INTERRUPT;
726
727 /* cancel the thread to be cancelled if defined */
728 if( condat->killthis != 0 ) {
729 pthread_cancel(condat->killthis);
730 /* The only possible error is ESRCH, so we do not care about it*/
731 }
732
733 /* call the function to be called if defined */
734 if( condat->execthis != NULL ) {
735 condat->execthis(condat->execargs);
736 }
737
738 /* quit */
739 break;
740 }
741 /* Otherwise dump input and continue */
742
743 }
744
745 /* Exit the watchdog thread, passing NULL as we don't expect a join */
746 pthread_exit(NULL);
747 }
748
749 /* SK_watchstart
750
751 starts sk_watchdog thread unless already started,
752 and registers its threadid in the condat structure
753
754 dies if watchdog already running
755 */
756 er_ret_t
757 SK_watchstart(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
758 {
759 dieif( condat->watchdog != 0 );
760
761 /* init the mutex in locked state, watchdog will unlock it when
762 it's ready for signal */
763 pthread_mutex_init( & condat->watchmutex, NULL );
764 pthread_mutex_lock( & condat->watchmutex );
765
766 pthread_create(&condat->watchdog, NULL, sk_watchdog, (void *) condat );
767
768 return SK_OK;
769 }
770
771
772 /* SK_watchstop
773
774 stops sk_watchdog thread if it is registered in the connection struct
775 */
776 er_ret_t
777 SK_watchstop(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
778 {
779 void *res;
780
781 if(condat->watchdog > 0) {
782 int ret;
783
784 /* wait until the watchdog is ready for signal */
785 pthread_mutex_lock( & condat->watchmutex );
786
787 ret = pthread_kill(condat->watchdog, SIGUSR1);
788
789 ret = pthread_join(condat->watchdog, &res);
790
791 pthread_mutex_destroy( & condat->watchmutex );
792 condat->watchdog = 0;
793 }
794 return SK_OK;
795 }
796
797 /* SK_watchkill
798
799 sets the threadid of the thread to be killed by watchdog
800 0 means dont kill anything
801 */
802 void
803 SK_watchkill(sk_conn_st *condat, pthread_t killthis)
/* [<][>][^][v][top][bottom][index][help] */
804 {
805 condat->killthis = killthis;
806 }
807
808 void
809 SK_watchexec( sk_conn_st *condat, void *(*function)(void *) , void *args)
/* [<][>][^][v][top][bottom][index][help] */
810 {
811 condat->execthis = function;
812 condat->execargs = args;
813 }
814
815 void
816 SK_watchclear(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
817 {
818 condat->execthis = NULL;
819 condat->execargs = NULL;
820 condat->killthis = 0;
821 }