1 /* $Id: tmesh.c,v 1.4 2009/08/30 17:06:38 fredette Exp $ */
2 
3 /* tmesh/tmesh.c - the tme shell: */
4 
5 /*
6  * Copyright (c) 2003 Matt Fredette
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Matt Fredette.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <tme/common.h>
37 _TME_RCSID("$Id: tmesh.c,v 1.4 2009/08/30 17:06:38 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/tme.h>
41 #include <tme/tmesh.h>
42 #include <tme/hash.h>
43 #include <stdio.h>
44 #include <string.h>
45 
46 /* macros: */
47 
48 /* the binary log message buffer size: */
49 #define _TMESH_LOG_MESSAGE_BINARY_BUFFER_SIZE	(5 * TME_LOG_MESSAGE_SIZE_MAX_BINARY)
50 
51 /* the binary log message handle, size, and errno: */
52 #define _TMESH_LOG_MESSAGE_BINARY_ERRNO		(0xff)
53 #if TME_LOG_MESSAGE_SIZE_MAX_BINARY & (TME_LOG_MESSAGE_SIZE_MAX_BINARY - 1)
54 #error "TME_LOG_MESSAGE_SIZE_MAX_BINARY must be a power of two"
55 #endif
56 #define _TMESH_LOG_MESSAGE_BINARY_SIZE \
57   ((TME_LOG_MESSAGE_SIZE_MAX_BINARY - 1) * (_TMESH_LOG_MESSAGE_BINARY_ERRNO + 1))
58 #define _TMESH_LOG_MESSAGE_BINARY_HANDLE \
59   (~ (tme_uint32_t) (_TMESH_LOG_MESSAGE_BINARY_SIZE + _TMESH_LOG_MESSAGE_BINARY_ERRNO))
60 
61 /* types: */
62 
63 /* an input buffer: */
64 struct _tmesh_input {
65   FILE *_tmesh_input_fp;
66   char _tmesh_input_buffer[1024];
67   unsigned int _tmesh_input_buffer_head;
68   unsigned int _tmesh_input_buffer_tail;
69   int _tmesh_input_buffer_eof;
70 };
71 
72 /* a binary log message: */
73 struct _tmesh_log_message_binary {
74   tme_uint32_t _tmesh_log_message_binary_handle_size_errno;
75   tme_uint32_t _tmesh_log_message_binary_level;
76 };
77 
78 /* globals: */
79 const char *argv0;
80 
81 /* our shell instance: */
82 static void *_tmesh;
83 
84 /* our current input: */
85 static struct tmesh_io *_tmesh_io;
86 
87 /* nonzero if we're doing the pre-threads commands: */
88 static int _tmesh_doing_pre_threads;
89 
90 /* our log and its mutex: */
91 static FILE *_tmesh_log;
92 static tme_mutex_t _tmesh_log_mutex;
93 
94 /* the log mode: */
95 static unsigned int _tmesh_log_mode = TME_LOG_MODE_TEXT;
96 
97 /* the next log handle number: */
98 static tme_uint32_t _tmesh_log_handle_next;
99 
100 /* a format hash: */
101 static tme_hash_t _tmesh_log_hash_format;
102 
103 /* this removes all consumed characters from a buffer, and shifts
104    everything else down: */
105 static void
_tmesh_remove_consumed(struct _tmesh_input * input)106 _tmesh_remove_consumed(struct _tmesh_input *input)
107 {
108   input->_tmesh_input_buffer_head -= input->_tmesh_input_buffer_tail;
109   memmove(input->_tmesh_input_buffer,
110 	  input->_tmesh_input_buffer
111 	  + input->_tmesh_input_buffer_tail,
112 	  input->_tmesh_input_buffer_head);
113   input->_tmesh_input_buffer_tail = 0;
114 }
115 
116 /* our pre-getc function: */
117 static int
_tmesh_pre_getc(struct tmesh_io * io)118 _tmesh_pre_getc(struct tmesh_io *io)
119 {
120   struct _tmesh_input *input;
121   int c;
122 
123   /* recover our input: */
124   input = io->tmesh_io_private;
125 
126   /* get the next character: */
127   c = getc(input->_tmesh_input_fp);
128 
129   return (c == EOF ? TMESH_C_EOF : c);
130 }
131 
132 /* our getc function: */
133 static int
_tmesh_getc(struct tmesh_io * io)134 _tmesh_getc(struct tmesh_io *io)
135 {
136   struct _tmesh_input *input;
137   int c;
138 
139   /* recover our input: */
140   input = io->tmesh_io_private;
141 
142   /* if the buffer isn't empty yet, return the next character: */
143   if (input->_tmesh_input_buffer_tail
144       < input->_tmesh_input_buffer_head) {
145     c = input->_tmesh_input_buffer[input->_tmesh_input_buffer_tail++];
146     return (c);
147   }
148 
149   /* if we're at EOF, return EOF: */
150   if (input->_tmesh_input_buffer_eof) {
151     return (TMESH_C_EOF);
152   }
153 
154   /* otherwise, we must yield: */
155   return (TMESH_C_YIELD);
156 }
157 
158 /* our close function: */
159 static void
_tmesh_close(struct tmesh_io * io_old,struct tmesh_io * io_new)160 _tmesh_close(struct tmesh_io *io_old, struct tmesh_io *io_new)
161 {
162   struct _tmesh_input *input;
163 
164   /* recover our input: */
165   input = io_old->tmesh_io_private;
166 
167   /* close the file and free the input: */
168   fclose(input->_tmesh_input_fp);
169   tme_free(input);
170 
171   /* set the new, emerging input: */
172   _tmesh_io = io_new;
173   _tmesh_remove_consumed(io_new->tmesh_io_private);
174 }
175 
176 /* our open function: */
177 static int
_tmesh_open(struct tmesh_io * io_new,struct tmesh_io * io_old,char ** _output)178 _tmesh_open(struct tmesh_io *io_new, struct tmesh_io *io_old, char **_output)
179 {
180   struct _tmesh_input *input;
181   int saved_errno;
182 
183   /* allocate a new input: */
184   input = tme_new0(struct _tmesh_input, 1);
185 
186   /* try to open the file: */
187   input->_tmesh_input_fp = fopen(io_new->tmesh_io_name, "r");
188 
189   /* if the open failed: */
190   if (input->_tmesh_input_fp == NULL) {
191     saved_errno = errno;
192     tme_free(input);
193     _tmesh_doing_pre_threads = FALSE;
194     return (saved_errno);
195   }
196 
197   /* set the new input: */
198   io_new->tmesh_io_private = input;
199   io_new->tmesh_io_getc = (_tmesh_doing_pre_threads
200 			   ? _tmesh_pre_getc
201 			   : _tmesh_getc);
202   io_new->tmesh_io_close = _tmesh_close;
203   io_new->tmesh_io_open = _tmesh_open;
204   _tmesh_io = io_new;
205   _tmesh_doing_pre_threads = FALSE;
206 
207   return (TME_OK);
208 }
209 
210 /* our log output function: */
211 static void
_tmesh_log_output(struct tme_log_handle * handle)212 _tmesh_log_output(struct tme_log_handle *handle)
213 {
214   FILE *fp;
215 
216   /* lock the log mutex: */
217   tme_mutex_lock(&_tmesh_log_mutex);
218 
219   /* if this is an error, report it to stderr, else it goes to the
220      log: */
221   fp = (handle->tme_log_handle_errno != TME_OK
222 	? stderr
223 	: _tmesh_log);
224 
225   fprintf(fp,
226 	  "[%s.%lu]",
227 	  (char *) handle->tme_log_handle_private,
228 	  handle->tme_log_handle_level);
229 
230   if (handle->tme_log_handle_message != NULL) {
231     fprintf(fp, ": %s", handle->tme_log_handle_message);
232     tme_free(handle->tme_log_handle_message);
233     handle->tme_log_handle_message = NULL;
234   }
235 
236   if (handle->tme_log_handle_errno != TME_OK) {
237     fprintf(fp, ": %s", strerror(handle->tme_log_handle_errno));
238   }
239 
240   fputc('\n', fp);
241 
242   /* unlock the log mutex: */
243   tme_mutex_unlock(&_tmesh_log_mutex);
244 }
245 
246 /* our binary log output function: */
247 static void
_tmesh_log_output_binary(struct tme_log_handle * handle)248 _tmesh_log_output_binary(struct tme_log_handle *handle)
249 {
250   struct _tmesh_log_message_binary *binary_message;
251   tme_uint32_t handle_size_errno;
252   struct _tmesh_log_message_binary binary_message_buffer;
253   tme_uint32_t buffer_size;
254 
255   /* lock the log mutex: */
256   tme_mutex_lock(&_tmesh_log_mutex);
257 
258   /* make values for the binary message header: */
259   binary_message = (struct _tmesh_log_message_binary *) handle->tme_log_handle_private;
260   handle_size_errno = binary_message->_tmesh_log_message_binary_handle_size_errno;
261   TME_FIELD_MASK_DEPOSITU(handle_size_errno,
262 			  _TMESH_LOG_MESSAGE_BINARY_SIZE,
263 			  (handle->tme_log_handle_message_size
264 			   - sizeof(*binary_message)));
265   TME_FIELD_MASK_DEPOSITU(handle_size_errno,
266 			  _TMESH_LOG_MESSAGE_BINARY_ERRNO,
267 			  handle->tme_log_handle_errno);
268 
269   /* write the binary message header: */
270   binary_message = (struct _tmesh_log_message_binary *) handle->tme_log_handle_message;
271   if (_TME_ALIGNOF_INT32_T == 1) {
272     binary_message->_tmesh_log_message_binary_handle_size_errno = handle_size_errno;
273     binary_message->_tmesh_log_message_binary_level = handle->tme_log_handle_level;
274   }
275   else {
276     binary_message_buffer._tmesh_log_message_binary_handle_size_errno = handle_size_errno;
277     binary_message_buffer._tmesh_log_message_binary_level = handle->tme_log_handle_level;
278     memcpy(binary_message,
279 	   &binary_message_buffer,
280 	   sizeof(binary_message_buffer));
281   }
282 
283   /* if there isn't enough room in the buffer for a maximum-sized
284      message: */
285   buffer_size
286     = ((((char *) binary_message)
287 	+ handle->tme_log_handle_message_size)
288        - (char *) handle->tme_log_handle_private);
289   if (buffer_size
290       > (_TMESH_LOG_MESSAGE_BINARY_BUFFER_SIZE
291 	 - TME_LOG_MESSAGE_SIZE_MAX_BINARY)) {
292 
293     /* write out the buffer: */
294     fwrite(handle->tme_log_handle_private,
295 	   1,
296 	   buffer_size,
297 	   _tmesh_log);
298 
299     /* reset the buffer: */
300     handle->tme_log_handle_message = handle->tme_log_handle_private;
301   }
302 
303   /* otherwise, there is enough room in the buffer for a maximum-sized
304      message: */
305   else {
306 
307     /* advance the buffer: */
308     handle->tme_log_handle_message += handle->tme_log_handle_message_size;
309   }
310 
311   /* reset for the next message: */
312   handle->tme_log_handle_message_size = sizeof(*binary_message);
313 
314   /* unlock the log mutex: */
315   tme_mutex_unlock(&_tmesh_log_mutex);
316 }
317 
318 /* our log open function: */
319 static void
_tmesh_log_open(struct tmesh_support * support,struct tme_log_handle * handle,const char * pathname,const char * module)320 _tmesh_log_open(struct tmesh_support *support,
321 		struct tme_log_handle *handle,
322 		const char *pathname,
323 		const char *module)
324 {
325   struct _tmesh_log_message_binary *binary_message;
326 
327   /* lock the log mutex: */
328   tme_mutex_lock(&_tmesh_log_mutex);
329 
330   handle->tme_log_handle_level_max = 0;
331   handle->tme_log_handle_mode = _tmesh_log_mode;
332 
333   /* if the log is binary: */
334   if (handle->tme_log_handle_mode == TME_LOG_MODE_BINARY) {
335 
336     /* allocate the binary buffer: */
337     handle->tme_log_handle_private = tme_malloc(_TMESH_LOG_MESSAGE_BINARY_BUFFER_SIZE);
338 
339     /* allocate the handle number and make the first message
340        structure: */
341     binary_message = (struct _tmesh_log_message_binary *) handle->tme_log_handle_private;
342     memset (binary_message, 0, sizeof(*binary_message));
343     TME_FIELD_MASK_DEPOSITU(binary_message->_tmesh_log_message_binary_handle_size_errno,
344 			    _TMESH_LOG_MESSAGE_BINARY_HANDLE,
345 			    _tmesh_log_handle_next);
346     _tmesh_log_handle_next++;
347     handle->tme_log_handle_message = (char *) binary_message;
348     handle->tme_log_handle_message_size = sizeof(*binary_message);
349     assert (handle->tme_log_handle_message_size < TME_LOG_MESSAGE_SIZE_MAX_BINARY);
350 
351     /* write a dummy first message for the handle that is only the
352        pathname: */
353     TME_FIELD_MASK_DEPOSITU(binary_message->_tmesh_log_message_binary_handle_size_errno,
354 			    _TMESH_LOG_MESSAGE_BINARY_SIZE,
355 			    strlen(pathname) + 1);
356     fwrite(binary_message,
357 	   sizeof(*binary_message),
358 	   1,
359 	   _tmesh_log);
360     fwrite(pathname,
361 	   1,
362 	   (strlen(pathname) + 1),
363 	   _tmesh_log);
364 
365     /* set the output function: */
366     handle->tme_log_handle_output = _tmesh_log_output_binary;
367 
368     /* set the format hash: */
369     handle->tme_log_handle_hash_format = _tmesh_log_hash_format;
370   }
371 
372   /* otherwise, the log is text: */
373   else {
374 
375     /* set the output function: */
376     handle->tme_log_handle_message = NULL;
377     handle->tme_log_handle_output = _tmesh_log_output;
378     handle->tme_log_handle_private = tme_strdup(pathname);
379   }
380 
381   /* unlock the log mutex: */
382   tme_mutex_unlock(&_tmesh_log_mutex);
383 }
384 
385 /* our log close function: */
386 static void
_tmesh_log_close(struct tmesh_support * support,struct tme_log_handle * handle)387 _tmesh_log_close(struct tmesh_support *support,
388 		 struct tme_log_handle *handle)
389 {
390   tme_free(handle->tme_log_handle_private);
391 }
392 
393 /* our thread: */
394 static void
_tmesh_thread(void * junk)395 _tmesh_thread(void *junk)
396 {
397   int yield, rc;
398   struct tmesh_io *io;
399   struct _tmesh_input *input;
400   char *output;
401   unsigned int consumed;
402 
403   /* loop while we have a current input buffer: */
404   for (; (io = _tmesh_io) != NULL;) {
405     input = io->tmesh_io_private;
406 
407     /* remove all consumed characters: */
408     _tmesh_remove_consumed(input);
409 
410     /* if the current input buffer is full, a command is too long: */
411     if (input->_tmesh_input_buffer_head
412 	== sizeof(input->_tmesh_input_buffer)) {
413       fprintf(stderr, "%s: command too long\n", argv0);
414       input->_tmesh_input_buffer_head = 0;
415     }
416 
417     /* try to read more input: */
418     rc = tme_thread_read_yield(fileno(input->_tmesh_input_fp),
419 			       input->_tmesh_input_buffer
420 			       + input->_tmesh_input_buffer_head,
421 			       sizeof(input->_tmesh_input_buffer)
422 			       - input->_tmesh_input_buffer_head);
423 
424     /* if the read failed: */
425     if (rc < 0) {
426       fprintf(stderr, "%s: %s\n",
427 	      io->tmesh_io_name,
428 	      strerror(errno));
429       continue;
430     }
431 
432     /* add characters in our current input buffer, or set EOF: */
433     if (rc > 0) {
434       input->_tmesh_input_buffer_head += rc;
435     }
436     else {
437       input->_tmesh_input_buffer_eof = TRUE;
438     }
439 
440     /* run commands until we have to yield: */
441     for (;;) {
442 
443       /* all characters already read have been consumed: */
444       consumed = input->_tmesh_input_buffer_tail;
445 
446       /* run a command: */
447       rc = tmesh_eval(_tmesh, &output, &yield);
448 
449       /* if we're yielding: */
450       if (yield) {
451 
452 	/* if the current io has not changed, mark how many
453 	   characters were consumed by successful commands: */
454 	if (io == _tmesh_io) {
455 	  input->_tmesh_input_buffer_tail = consumed;
456 	}
457 
458 	break;
459       }
460 
461       /* this command may have changed the current io, so reload: */
462       io = _tmesh_io;
463       input = io->tmesh_io_private;
464 
465       /* display this command's output: */
466       if (rc == TME_OK) {
467 	if (output != NULL
468 	    && *output != '\0') {
469 	  printf("%s\n", output);
470 	}
471       }
472       else {
473 	fprintf(stderr, "%s:%lu: ",
474 		io->tmesh_io_name,
475 		io->tmesh_io_input_line);
476 	if (output != NULL
477 	    && *output != '\0') {
478 	  fprintf(stderr, "%s: ", output);
479 	}
480 	fprintf(stderr, "%s\n", strerror(rc));
481       }
482       if (output != NULL) {
483 	tme_free(output);
484       }
485 
486       /* put up the next prompt: */
487       if (isatty(fileno(input->_tmesh_input_fp))
488 	  && isatty(fileno(stdout))) {
489 	printf("%s> ", argv0);
490 	fflush(stdout);
491       }
492     }
493   }
494 }
495 
496 int
main(int argc,char ** argv)497 main(int argc, char **argv)
498 {
499   int usage;
500   const char *opt;
501   int arg_i;
502   const char *pre_threads_filename;
503   const char *log_filename;
504   int interactive;
505   struct tmesh_io io;
506   struct tmesh_support support;
507   struct _tmesh_input *input_stdin;
508   char *output;
509   int yield, rc;
510 
511   /* check our command line: */
512   usage = FALSE;
513   pre_threads_filename = NULL;
514   log_filename = "/dev/null";
515   interactive = TRUE;
516   if ((argv0 = strrchr(argv[0], '/')) == NULL) argv0 = argv[0]; else argv0++;
517   for (arg_i = 1;
518        (arg_i < argc
519 	&& *argv[arg_i] == '-');
520        arg_i++) {
521     opt = argv[arg_i];
522     if (!strcmp(opt, "--log")) {
523       if (++arg_i < argc) {
524 	log_filename = argv[arg_i];
525       }
526       else {
527 	usage = TRUE;
528 	break;
529       }
530     }
531     else if (!strcmp(opt, "--log-mode")) {
532       ++arg_i;
533       if (arg_i >= argc
534 	  || strcmp(argv[arg_i], "binary")) {
535 	usage = TRUE;
536 	break;
537       }
538       _tmesh_log_mode = TME_LOG_MODE_BINARY;
539       if (_tmesh_log_hash_format == NULL) {
540 	_tmesh_log_hash_format = tme_hash_new(tme_direct_hash, tme_direct_compare, TME_HASH_DATA_NULL);
541       }
542     }
543     else if (!strcmp(opt, "-c")
544 	     || !strcmp(opt, "--noninteractive")) {
545       interactive = FALSE;
546     }
547     else {
548       if (strcmp(opt, "-h")
549 	  && strcmp(opt, "--help")
550 	  && strcmp(opt, "-h")) {
551 	fprintf(stderr, "%s: unknown option %s\n",
552 		argv0, opt);
553       }
554       usage = TRUE;
555       break;
556     }
557   }
558   if (arg_i < argc) {
559     pre_threads_filename = argv[arg_i++];
560   }
561   else {
562     usage = TRUE;
563   }
564   if (usage) {
565     fprintf(stderr, "\
566 usage: %s [OPTIONS] INITIAL-CONFIG\n\
567 where OPTIONS are:\n\
568   --log LOGFILE          log to LOGFILE\n\
569   -c, --noninteractive   read no commands from standard input\n\
570 ",
571 	    argv0);
572     exit(1);
573   }
574 
575   if (!strcmp(log_filename, "-")) {
576     _tmesh_log = stdout;
577   }
578   else {
579     _tmesh_log = fopen(log_filename, "a");
580     if (_tmesh_log == NULL) {
581       perror(log_filename);
582       exit(1);
583     }
584   }
585 
586   /* initialize libtme: */
587   (void) tme_init();
588 
589   /* initialize libtmesh: */
590   (void) tmesh_init();
591 
592   /* create our stdin input buffer, and stuff it with the command to
593      source the pre-threads commands: */
594   input_stdin = tme_new0(struct _tmesh_input, 1);
595   input_stdin->_tmesh_input_fp = stdin;
596   snprintf(input_stdin->_tmesh_input_buffer,
597 	   sizeof(input_stdin->_tmesh_input_buffer) - 1,
598 	   "source %s\n",
599 	   pre_threads_filename);
600   input_stdin->_tmesh_input_buffer[sizeof(input_stdin->_tmesh_input_buffer) - 1] = '\0';
601   input_stdin->_tmesh_input_buffer_head = strlen(input_stdin->_tmesh_input_buffer);
602 
603   /* create our stdin io: */
604   io.tmesh_io_name = "*stdin*";
605   io.tmesh_io_private = input_stdin;
606   io.tmesh_io_input_line = 0;
607   io.tmesh_io_getc = _tmesh_getc;
608   io.tmesh_io_close = _tmesh_close;
609   io.tmesh_io_open = _tmesh_open;
610   _tmesh_io = &io;
611 
612   /* the next open we do will be for the pre-threads commands: */
613   _tmesh_doing_pre_threads = TRUE;
614 
615   /* create our support: */
616   tme_mutex_init(&_tmesh_log_mutex);
617   support.tmesh_support_log_open = _tmesh_log_open;
618   support.tmesh_support_log_close = _tmesh_log_close;
619 
620   /* create our shell: */
621   _tmesh = tmesh_new(&support, &io);
622 
623   /* run commands until we get a yield: */
624   for (;;) {
625     rc = tmesh_eval(_tmesh, &output, &yield);
626     if (yield) {
627       break;
628     }
629     if (rc == TME_OK) {
630       if (output != NULL
631 	  && *output != '\0') {
632 	printf("%s\n", output);
633       }
634     }
635     else {
636       fprintf(stderr, "%s:%lu: ",
637 	      _tmesh_io->tmesh_io_name,
638 	      _tmesh_io->tmesh_io_input_line);
639       if (output != NULL
640 	  && *output != '\0') {
641 	fprintf(stderr, "%s: ", output);
642       }
643       fprintf(stderr, "%s\n", strerror(rc));
644     }
645     if (output != NULL) {
646       tme_free(output);
647     }
648   }
649 
650   /* if we're interactive: */
651   if (interactive) {
652 
653     /* put up our first prompt: */
654     if (isatty(fileno(stdin))
655 	&& isatty(fileno(stdout))) {
656       printf("%s> ", argv0);
657       fflush(stdout);
658     }
659 
660     /* create our thread: */
661     tme_thread_create((tme_thread_t) _tmesh_thread, NULL);
662   }
663 
664   /* run the threads: */
665   tme_threads_run();
666 
667   /* done: */
668   exit(0);
669 }
670