Jack2
1.9.10
|
00001 // u/* -*- Mode: C++ ; c-basic-offset: 4 -*- */ 00002 /* 00003 JACK control API implementation 00004 00005 Copyright (C) 2008 Nedko Arnaudov 00006 Copyright (C) 2008 Grame 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; version 2 of the License. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software 00019 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00020 00021 */ 00022 00023 #ifndef WIN32 00024 #include <stdint.h> 00025 #include <dirent.h> 00026 #include <pthread.h> 00027 #endif 00028 00029 #include "types.h" 00030 #include <string.h> 00031 #include <errno.h> 00032 #include <stdio.h> 00033 #include <assert.h> 00034 #include <signal.h> 00035 #include <sys/utsname.h> 00036 00037 #include "jslist.h" 00038 #include "driver_interface.h" 00039 #include "JackError.h" 00040 #include "JackServer.h" 00041 #include "shm.h" 00042 #include "JackTools.h" 00043 #include "JackControlAPI.h" 00044 #include "JackLockedEngine.h" 00045 #include "JackConstants.h" 00046 #include "JackDriverLoader.h" 00047 #include "JackServerGlobals.h" 00048 00049 using namespace Jack; 00050 00051 /* JackEngine::CheckPortsConnect() has some assumptions about char values */ 00052 static struct jack_constraint_enum_char_descriptor self_connect_mode_constraint_descr_array[] = 00053 { 00054 { ' ', "Don't restrict self connect requests" }, 00055 { 'E', "Fail self connect requests to external ports only" }, 00056 { 'e', "Ignore self connect requests to external ports only" }, 00057 { 'A', "Fail all self connect requests" }, 00058 { 'a', "Ignore all self connect requests" }, 00059 { 0 } 00060 }; 00061 00062 struct jackctl_server 00063 { 00064 JSList * drivers; 00065 JSList * internals; 00066 JSList * parameters; 00067 00068 class JackServer * engine; 00069 00070 /* string, server name */ 00071 union jackctl_parameter_value name; 00072 union jackctl_parameter_value default_name; 00073 00074 /* bool, whether to be "realtime" */ 00075 union jackctl_parameter_value realtime; 00076 union jackctl_parameter_value default_realtime; 00077 00078 /* int32_t */ 00079 union jackctl_parameter_value realtime_priority; 00080 union jackctl_parameter_value default_realtime_priority; 00081 00082 /* bool, whether to exit once all clients have closed their connections */ 00083 union jackctl_parameter_value temporary; 00084 union jackctl_parameter_value default_temporary; 00085 00086 /* bool, whether to be verbose */ 00087 union jackctl_parameter_value verbose; 00088 union jackctl_parameter_value default_verbose; 00089 00090 /* int32_t, msecs; if zero, use period size. */ 00091 union jackctl_parameter_value client_timeout; 00092 union jackctl_parameter_value default_client_timeout; 00093 00094 /* uint32_t, clock source type */ 00095 union jackctl_parameter_value clock_source; 00096 union jackctl_parameter_value default_clock_source; 00097 00098 /* uint32_t, max port number */ 00099 union jackctl_parameter_value port_max; 00100 union jackctl_parameter_value default_port_max; 00101 00102 /* bool */ 00103 union jackctl_parameter_value replace_registry; 00104 union jackctl_parameter_value default_replace_registry; 00105 00106 /* bool, synchronous or asynchronous engine mode */ 00107 union jackctl_parameter_value sync; 00108 union jackctl_parameter_value default_sync; 00109 00110 /* char enum, self connect mode mode */ 00111 union jackctl_parameter_value self_connect_mode; 00112 union jackctl_parameter_value default_self_connect_mode; 00113 }; 00114 00115 struct jackctl_driver 00116 { 00117 jack_driver_desc_t * desc_ptr; 00118 JSList * parameters; 00119 JSList * infos; 00120 }; 00121 00122 struct jackctl_internal 00123 { 00124 jack_driver_desc_t * desc_ptr; 00125 JSList * parameters; 00126 int refnum; 00127 }; 00128 00129 struct jackctl_parameter 00130 { 00131 const char * name; 00132 const char * short_description; 00133 const char * long_description; 00134 jackctl_param_type_t type; 00135 bool is_set; 00136 union jackctl_parameter_value * value_ptr; 00137 union jackctl_parameter_value * default_value_ptr; 00138 00139 union jackctl_parameter_value value; 00140 union jackctl_parameter_value default_value; 00141 struct jackctl_driver * driver_ptr; 00142 char id; 00143 jack_driver_param_constraint_desc_t * constraint_ptr; 00144 }; 00145 00146 const char * jack_get_self_connect_mode_description(char mode) 00147 { 00148 struct jack_constraint_enum_char_descriptor * descr_ptr; 00149 00150 for (descr_ptr = self_connect_mode_constraint_descr_array; 00151 descr_ptr->value; 00152 descr_ptr++) 00153 if (descr_ptr->value == mode) return descr_ptr->short_desc; 00154 00155 return NULL; 00156 } 00157 00158 static 00159 struct jackctl_parameter * 00160 jackctl_add_parameter( 00161 JSList ** parameters_list_ptr_ptr, 00162 const char * name, 00163 const char * short_description, 00164 const char * long_description, 00165 jackctl_param_type_t type, 00166 union jackctl_parameter_value * value_ptr, 00167 union jackctl_parameter_value * default_value_ptr, 00168 union jackctl_parameter_value value, 00169 jack_driver_param_constraint_desc_t * constraint_ptr = NULL) 00170 { 00171 struct jackctl_parameter * parameter_ptr; 00172 00173 parameter_ptr = (struct jackctl_parameter *)malloc(sizeof(struct jackctl_parameter)); 00174 if (parameter_ptr == NULL) 00175 { 00176 jack_error("Cannot allocate memory for jackctl_parameter structure."); 00177 goto fail; 00178 } 00179 00180 parameter_ptr->name = name; 00181 parameter_ptr->short_description = short_description; 00182 parameter_ptr->long_description = long_description; 00183 parameter_ptr->type = type; 00184 parameter_ptr->is_set = false; 00185 00186 if (value_ptr == NULL) 00187 { 00188 value_ptr = ¶meter_ptr->value; 00189 } 00190 00191 if (default_value_ptr == NULL) 00192 { 00193 default_value_ptr = ¶meter_ptr->default_value; 00194 } 00195 00196 parameter_ptr->value_ptr = value_ptr; 00197 parameter_ptr->default_value_ptr = default_value_ptr; 00198 00199 *value_ptr = *default_value_ptr = value; 00200 00201 parameter_ptr->driver_ptr = NULL; 00202 parameter_ptr->id = 0; 00203 parameter_ptr->constraint_ptr = constraint_ptr; 00204 00205 *parameters_list_ptr_ptr = jack_slist_append(*parameters_list_ptr_ptr, parameter_ptr); 00206 00207 return parameter_ptr; 00208 00209 fail: 00210 return NULL; 00211 } 00212 00213 static 00214 void 00215 jackctl_free_driver_parameters( 00216 struct jackctl_driver * driver_ptr) 00217 { 00218 JSList * next_node_ptr; 00219 00220 while (driver_ptr->parameters) 00221 { 00222 next_node_ptr = driver_ptr->parameters->next; 00223 free(driver_ptr->parameters->data); 00224 free(driver_ptr->parameters); 00225 driver_ptr->parameters = next_node_ptr; 00226 } 00227 } 00228 00229 static 00230 bool 00231 jackctl_add_driver_parameters( 00232 struct jackctl_driver * driver_ptr) 00233 { 00234 unsigned int i; 00235 00236 union jackctl_parameter_value jackctl_value; 00237 jackctl_param_type_t jackctl_type; 00238 struct jackctl_parameter * parameter_ptr; 00239 jack_driver_param_desc_t * descriptor_ptr; 00240 00241 for (i = 0 ; i < driver_ptr->desc_ptr->nparams ; i++) 00242 { 00243 descriptor_ptr = driver_ptr->desc_ptr->params + i; 00244 00245 switch (descriptor_ptr->type) 00246 { 00247 case JackDriverParamInt: 00248 jackctl_type = JackParamInt; 00249 jackctl_value.i = descriptor_ptr->value.i; 00250 break; 00251 case JackDriverParamUInt: 00252 jackctl_type = JackParamUInt; 00253 jackctl_value.ui = descriptor_ptr->value.ui; 00254 break; 00255 case JackDriverParamChar: 00256 jackctl_type = JackParamChar; 00257 jackctl_value.c = descriptor_ptr->value.c; 00258 break; 00259 case JackDriverParamString: 00260 jackctl_type = JackParamString; 00261 strcpy(jackctl_value.str, descriptor_ptr->value.str); 00262 break; 00263 case JackDriverParamBool: 00264 jackctl_type = JackParamBool; 00265 jackctl_value.b = descriptor_ptr->value.i; 00266 break; 00267 default: 00268 jack_error("Unknown driver parameter type %i", (int)descriptor_ptr->type); 00269 assert(0); 00270 goto fail; 00271 } 00272 00273 parameter_ptr = jackctl_add_parameter( 00274 &driver_ptr->parameters, 00275 descriptor_ptr->name, 00276 descriptor_ptr->short_desc, 00277 descriptor_ptr->long_desc, 00278 jackctl_type, 00279 NULL, 00280 NULL, 00281 jackctl_value, 00282 descriptor_ptr->constraint); 00283 00284 if (parameter_ptr == NULL) 00285 { 00286 goto fail; 00287 } 00288 00289 parameter_ptr->driver_ptr = driver_ptr; 00290 parameter_ptr->id = descriptor_ptr->character; 00291 } 00292 00293 return true; 00294 00295 fail: 00296 jackctl_free_driver_parameters(driver_ptr); 00297 00298 return false; 00299 } 00300 00301 /* destroy jack_driver_param_desc_t list created by jackctl_create_param_list() */ 00302 static void 00303 jackctl_destroy_param_list( 00304 JSList * params) 00305 { 00306 JSList * next; 00307 00308 while (params) 00309 { 00310 next = params->next; 00311 free(params->data); 00312 free(params); 00313 params = next; 00314 } 00315 } 00316 00317 /* for drivers and internals are configured through jack_driver_param_t JSList */ 00318 /* this function creates such list from a jackctl_parameter list */ 00319 static 00320 bool 00321 jackctl_create_param_list( 00322 const JSList * paramlist, 00323 JSList ** retparamlist) 00324 { 00325 jackctl_parameter * param_ptr; 00326 jack_driver_param_t * retparam_ptr; 00327 00328 *retparamlist = NULL; 00329 while (paramlist != NULL) 00330 { 00331 param_ptr = (jackctl_parameter *)paramlist->data; 00332 if (param_ptr->is_set) 00333 { 00334 /* jack_info("setting driver parameter %p ...", parameter_ptr); */ 00335 retparam_ptr = (jack_driver_param_t *)malloc(sizeof(jack_driver_param_t)); 00336 if (retparam_ptr == NULL) 00337 { 00338 jack_error ("Allocation of jack_driver_param_t structure failed"); 00339 goto destroy; 00340 } 00341 00342 retparam_ptr->character = param_ptr->id; 00343 00344 switch (param_ptr->type) 00345 { 00346 case JackParamInt: 00347 retparam_ptr->value.i = param_ptr->value_ptr->i; 00348 break; 00349 case JackParamUInt: 00350 retparam_ptr->value.ui = param_ptr->value_ptr->ui; 00351 break; 00352 case JackParamChar: 00353 retparam_ptr->value.c = param_ptr->value_ptr->c; 00354 break; 00355 case JackParamString: 00356 strcpy(retparam_ptr->value.str, param_ptr->value_ptr->str); 00357 break; 00358 case JackParamBool: 00359 retparam_ptr->value.i = param_ptr->value_ptr->b; 00360 break; 00361 default: 00362 jack_error("Unknown parameter type %i", (int)param_ptr->type); 00363 assert(0); 00364 goto free; 00365 } 00366 00367 *retparamlist = jack_slist_append(*retparamlist, retparam_ptr); 00368 } 00369 00370 paramlist = paramlist->next; 00371 } 00372 00373 return true; 00374 00375 free: 00376 free(retparam_ptr); 00377 destroy: 00378 jackctl_destroy_param_list(*retparamlist); 00379 return false; 00380 } 00381 00382 static int 00383 jackctl_drivers_load( 00384 struct jackctl_server * server_ptr) 00385 { 00386 struct jackctl_driver * driver_ptr; 00387 JSList *node_ptr; 00388 JSList *descriptor_node_ptr; 00389 00390 descriptor_node_ptr = jack_drivers_load(NULL); 00391 if (descriptor_node_ptr == NULL) 00392 { 00393 jack_error("Could not find any drivers in driver directory!"); 00394 return false; 00395 } 00396 00397 while (descriptor_node_ptr != NULL) 00398 { 00399 driver_ptr = (struct jackctl_driver *)malloc(sizeof(struct jackctl_driver)); 00400 if (driver_ptr == NULL) 00401 { 00402 jack_error("Memory allocation of jackctl_driver structure failed."); 00403 goto next; 00404 } 00405 00406 driver_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; 00407 driver_ptr->parameters = NULL; 00408 driver_ptr->infos = NULL; 00409 00410 if (!jackctl_add_driver_parameters(driver_ptr)) 00411 { 00412 assert(driver_ptr->parameters == NULL); 00413 free(driver_ptr); 00414 goto next; 00415 } 00416 00417 server_ptr->drivers = jack_slist_append(server_ptr->drivers, driver_ptr); 00418 00419 next: 00420 node_ptr = descriptor_node_ptr; 00421 descriptor_node_ptr = descriptor_node_ptr->next; 00422 free(node_ptr); 00423 } 00424 00425 return true; 00426 } 00427 00428 static 00429 void 00430 jackctl_server_free_drivers( 00431 struct jackctl_server * server_ptr) 00432 { 00433 JSList * next_node_ptr; 00434 struct jackctl_driver * driver_ptr; 00435 00436 while (server_ptr->drivers) 00437 { 00438 next_node_ptr = server_ptr->drivers->next; 00439 driver_ptr = (struct jackctl_driver *)server_ptr->drivers->data; 00440 00441 jackctl_free_driver_parameters(driver_ptr); 00442 free(driver_ptr->desc_ptr->params); 00443 free(driver_ptr->desc_ptr); 00444 free(driver_ptr); 00445 00446 free(server_ptr->drivers); 00447 server_ptr->drivers = next_node_ptr; 00448 } 00449 } 00450 00451 static int 00452 jackctl_internals_load( 00453 struct jackctl_server * server_ptr) 00454 { 00455 struct jackctl_internal * internal_ptr; 00456 JSList *node_ptr; 00457 JSList *descriptor_node_ptr; 00458 00459 descriptor_node_ptr = jack_internals_load(NULL); 00460 if (descriptor_node_ptr == NULL) 00461 { 00462 jack_error("Could not find any internals in driver directory!"); 00463 return false; 00464 } 00465 00466 while (descriptor_node_ptr != NULL) 00467 { 00468 internal_ptr = (struct jackctl_internal *)malloc(sizeof(struct jackctl_internal)); 00469 if (internal_ptr == NULL) 00470 { 00471 jack_error("Memory allocation of jackctl_driver structure failed."); 00472 goto next; 00473 } 00474 00475 internal_ptr->desc_ptr = (jack_driver_desc_t *)descriptor_node_ptr->data; 00476 internal_ptr->parameters = NULL; 00477 internal_ptr->refnum = -1; 00478 00479 if (!jackctl_add_driver_parameters((struct jackctl_driver *)internal_ptr)) 00480 { 00481 assert(internal_ptr->parameters == NULL); 00482 free(internal_ptr); 00483 goto next; 00484 } 00485 00486 server_ptr->internals = jack_slist_append(server_ptr->internals, internal_ptr); 00487 00488 next: 00489 node_ptr = descriptor_node_ptr; 00490 descriptor_node_ptr = descriptor_node_ptr->next; 00491 free(node_ptr); 00492 } 00493 00494 return true; 00495 } 00496 00497 static 00498 void 00499 jackctl_server_free_internals( 00500 struct jackctl_server * server_ptr) 00501 { 00502 JSList * next_node_ptr; 00503 struct jackctl_internal * internal_ptr; 00504 00505 while (server_ptr->internals) 00506 { 00507 next_node_ptr = server_ptr->internals->next; 00508 internal_ptr = (struct jackctl_internal *)server_ptr->internals->data; 00509 00510 jackctl_free_driver_parameters((struct jackctl_driver *)internal_ptr); 00511 free(internal_ptr->desc_ptr->params); 00512 free(internal_ptr->desc_ptr); 00513 free(internal_ptr); 00514 00515 free(server_ptr->internals); 00516 server_ptr->internals = next_node_ptr; 00517 } 00518 } 00519 00520 static 00521 void 00522 jackctl_server_free_parameters( 00523 struct jackctl_server * server_ptr) 00524 { 00525 JSList * next_node_ptr; 00526 00527 while (server_ptr->parameters) 00528 { 00529 next_node_ptr = server_ptr->parameters->next; 00530 free(server_ptr->parameters->data); 00531 free(server_ptr->parameters); 00532 server_ptr->parameters = next_node_ptr; 00533 } 00534 } 00535 00536 #ifdef WIN32 00537 00538 struct jackctl_sigmask 00539 { 00540 HANDLE wait_event; 00541 }; 00542 00543 static jackctl_sigmask sigmask; 00544 00545 static void signal_handler(int signum) 00546 { 00547 printf("Jack main caught signal %d\n", signum); 00548 (void) signal(SIGINT, SIG_DFL); 00549 SetEvent(sigmask.wait_event); 00550 } 00551 00552 jackctl_sigmask_t * 00553 jackctl_setup_signals( 00554 unsigned int flags) 00555 { 00556 if ((sigmask.wait_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) { 00557 jack_error("CreateEvent fails err = %ld", GetLastError()); 00558 return 0; 00559 } 00560 00561 (void) signal(SIGINT, signal_handler); 00562 (void) signal(SIGABRT, signal_handler); 00563 (void) signal(SIGTERM, signal_handler); 00564 00565 return &sigmask; 00566 } 00567 00568 void jackctl_wait_signals(jackctl_sigmask_t * signals) 00569 { 00570 if (WaitForSingleObject(signals->wait_event, INFINITE) != WAIT_OBJECT_0) { 00571 jack_error("WaitForSingleObject fails err = %ld", GetLastError()); 00572 } 00573 } 00574 00575 #else 00576 00577 struct jackctl_sigmask 00578 { 00579 sigset_t signals; 00580 }; 00581 00582 static jackctl_sigmask sigmask; 00583 00584 static 00585 void 00586 signal_handler(int sig) 00587 { 00588 /* this is used by the child (active) process, but it never 00589 gets called unless we are already shutting down after 00590 another signal. 00591 */ 00592 char buf[64]; 00593 snprintf(buf, sizeof(buf), "Received signal %d during shutdown (ignored)\n", sig); 00594 } 00595 00596 SERVER_EXPORT jackctl_sigmask_t * 00597 jackctl_setup_signals( 00598 unsigned int flags) 00599 { 00600 sigset_t allsignals; 00601 struct sigaction action; 00602 int i; 00603 00604 /* ensure that we are in our own process group so that 00605 kill (SIG, -pgrp) does the right thing. 00606 */ 00607 00608 setsid(); 00609 00610 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 00611 00612 /* what's this for? 00613 00614 POSIX says that signals are delivered like this: 00615 00616 * if a thread has blocked that signal, it is not 00617 a candidate to receive the signal. 00618 * of all threads not blocking the signal, pick 00619 one at random, and deliver the signal. 00620 00621 this means that a simple-minded multi-threaded program can 00622 expect to get POSIX signals delivered randomly to any one 00623 of its threads, 00624 00625 here, we block all signals that we think we might receive 00626 and want to catch. all "child" threads will inherit this 00627 setting. if we create a thread that calls sigwait() on the 00628 same set of signals, implicitly unblocking all those 00629 signals. any of those signals that are delivered to the 00630 process will be delivered to that thread, and that thread 00631 alone. this makes cleanup for a signal-driven exit much 00632 easier, since we know which thread is doing it and more 00633 importantly, we are free to call async-unsafe functions, 00634 because the code is executing in normal thread context 00635 after a return from sigwait(). 00636 */ 00637 00638 sigemptyset(&sigmask.signals); 00639 sigaddset(&sigmask.signals, SIGHUP); 00640 sigaddset(&sigmask.signals, SIGINT); 00641 sigaddset(&sigmask.signals, SIGQUIT); 00642 sigaddset(&sigmask.signals, SIGPIPE); 00643 sigaddset(&sigmask.signals, SIGTERM); 00644 #ifndef __ANDROID__ 00645 /* android's bionic c doesn't provide pthread_cancel() and related functions. 00646 * to solve this issue, use pthread_kill() & SIGUSR1 instead. 00647 */ 00648 sigaddset(&sigmask.signals, SIGUSR1); 00649 #endif 00650 sigaddset(&sigmask.signals, SIGUSR2); 00651 00652 /* all child threads will inherit this mask unless they 00653 * explicitly reset it 00654 */ 00655 00656 pthread_sigmask(SIG_BLOCK, &sigmask.signals, 0); 00657 00658 /* install a do-nothing handler because otherwise pthreads 00659 behaviour is undefined when we enter sigwait. 00660 */ 00661 00662 sigfillset(&allsignals); 00663 action.sa_handler = signal_handler; 00664 action.sa_mask = allsignals; 00665 action.sa_flags = SA_RESTART|SA_RESETHAND; 00666 00667 for (i = 1; i < NSIG; i++) 00668 { 00669 if (sigismember (&sigmask.signals, i)) 00670 { 00671 sigaction(i, &action, 0); 00672 } 00673 } 00674 00675 return &sigmask; 00676 } 00677 00678 SERVER_EXPORT void 00679 jackctl_wait_signals(jackctl_sigmask_t * sigmask) 00680 { 00681 int sig; 00682 bool waiting = true; 00683 00684 while (waiting) { 00685 #if defined(sun) && !defined(__sun__) // SUN compiler only, to check 00686 sigwait(&sigmask->signals); 00687 #else 00688 sigwait(&sigmask->signals, &sig); 00689 #endif 00690 fprintf(stderr, "Jack main caught signal %d\n", sig); 00691 00692 switch (sig) { 00693 case SIGUSR1: 00694 //jack_dump_configuration(engine, 1); 00695 break; 00696 case SIGUSR2: 00697 // driver exit 00698 waiting = false; 00699 break; 00700 case SIGTTOU: 00701 break; 00702 default: 00703 waiting = false; 00704 break; 00705 } 00706 } 00707 00708 if (sig != SIGSEGV) { 00709 // unblock signals so we can see them during shutdown. 00710 // this will help prod developers not to lose sight of 00711 // bugs that cause segfaults etc. during shutdown. 00712 sigprocmask(SIG_UNBLOCK, &sigmask->signals, 0); 00713 } 00714 } 00715 #endif 00716 00717 static 00718 jack_driver_param_constraint_desc_t * 00719 get_realtime_priority_constraint() 00720 { 00721 jack_driver_param_constraint_desc_t * constraint_ptr; 00722 int min, max; 00723 00724 if (!jack_get_thread_realtime_priority_range(&min, &max)) 00725 { 00726 return NULL; 00727 } 00728 00729 //jack_info("realtime priority range is (%d,%d)", min, max); 00730 00731 constraint_ptr = (jack_driver_param_constraint_desc_t *)calloc(1, sizeof(jack_driver_param_constraint_desc_t)); 00732 if (constraint_ptr == NULL) 00733 { 00734 jack_error("Cannot allocate memory for jack_driver_param_constraint_desc_t structure."); 00735 return NULL; 00736 } 00737 constraint_ptr->flags = JACK_CONSTRAINT_FLAG_RANGE; 00738 00739 constraint_ptr->constraint.range.min.i = min; 00740 constraint_ptr->constraint.range.max.i = max; 00741 00742 return constraint_ptr; 00743 } 00744 00745 SERVER_EXPORT jackctl_server_t * jackctl_server_create( 00746 bool (* on_device_acquire)(const char * device_name), 00747 void (* on_device_release)(const char * device_name)) 00748 { 00749 struct jackctl_server * server_ptr; 00750 union jackctl_parameter_value value; 00751 00752 server_ptr = (struct jackctl_server *)malloc(sizeof(struct jackctl_server)); 00753 if (server_ptr == NULL) 00754 { 00755 jack_error("Cannot allocate memory for jackctl_server structure."); 00756 goto fail; 00757 } 00758 00759 server_ptr->drivers = NULL; 00760 server_ptr->internals = NULL; 00761 server_ptr->parameters = NULL; 00762 server_ptr->engine = NULL; 00763 00764 strcpy(value.str, JackTools::DefaultServerName()); 00765 if (jackctl_add_parameter( 00766 &server_ptr->parameters, 00767 "name", 00768 "Server name to use.", 00769 "", 00770 JackParamString, 00771 &server_ptr->name, 00772 &server_ptr->default_name, 00773 value) == NULL) 00774 { 00775 goto fail_free_parameters; 00776 } 00777 00778 value.b = true; 00779 if (jackctl_add_parameter( 00780 &server_ptr->parameters, 00781 "realtime", 00782 "Whether to use realtime mode.", 00783 "Use realtime scheduling. This is needed for reliable low-latency performance. On most systems, it requires JACK to run with special scheduler and memory allocation privileges, which may be obtained in several ways. On Linux you should use PAM.", 00784 JackParamBool, 00785 &server_ptr->realtime, 00786 &server_ptr->default_realtime, 00787 value) == NULL) 00788 { 00789 goto fail_free_parameters; 00790 } 00791 00792 struct utsname utsname; 00793 int success; 00794 success = uname( &utsname ); 00795 if( success == 0 && strstr( utsname.version, "ccrma" ) ) 00796 value.i = 60; 00797 else 00798 value.i = 20; 00799 if (jackctl_add_parameter( 00800 &server_ptr->parameters, 00801 "realtime-priority", 00802 "Scheduler priority when running in realtime mode.", 00803 "", 00804 JackParamInt, 00805 &server_ptr->realtime_priority, 00806 &server_ptr->default_realtime_priority, 00807 value, 00808 get_realtime_priority_constraint()) == NULL) 00809 { 00810 goto fail_free_parameters; 00811 } 00812 00813 value.b = false; 00814 if (jackctl_add_parameter( 00815 &server_ptr->parameters, 00816 "temporary", 00817 "Exit once all clients have closed their connections.", 00818 "", 00819 JackParamBool, 00820 &server_ptr->temporary, 00821 &server_ptr->default_temporary, 00822 value) == NULL) 00823 { 00824 goto fail_free_parameters; 00825 } 00826 00827 value.b = false; 00828 if (jackctl_add_parameter( 00829 &server_ptr->parameters, 00830 "verbose", 00831 "Verbose mode.", 00832 "", 00833 JackParamBool, 00834 &server_ptr->verbose, 00835 &server_ptr->default_verbose, 00836 value) == NULL) 00837 { 00838 goto fail_free_parameters; 00839 } 00840 00841 value.i = 0; 00842 if (jackctl_add_parameter( 00843 &server_ptr->parameters, 00844 "client-timeout", 00845 "Client timeout limit in milliseconds.", 00846 "", 00847 JackParamInt, 00848 &server_ptr->client_timeout, 00849 &server_ptr->default_client_timeout, 00850 value) == NULL) 00851 { 00852 goto fail_free_parameters; 00853 } 00854 00855 value.ui = 0; 00856 if (jackctl_add_parameter( 00857 &server_ptr->parameters, 00858 "clock-source", 00859 "Clocksource type : c(ycle) | h(pet) | s(ystem).", 00860 "", 00861 JackParamUInt, 00862 &server_ptr->clock_source, 00863 &server_ptr->default_clock_source, 00864 value) == NULL) 00865 { 00866 goto fail_free_parameters; 00867 } 00868 00869 value.ui = PORT_NUM; 00870 if (jackctl_add_parameter( 00871 &server_ptr->parameters, 00872 "port-max", 00873 "Maximum number of ports.", 00874 "", 00875 JackParamUInt, 00876 &server_ptr->port_max, 00877 &server_ptr->default_port_max, 00878 value) == NULL) 00879 { 00880 goto fail_free_parameters; 00881 } 00882 00883 value.b = false; 00884 if (jackctl_add_parameter( 00885 &server_ptr->parameters, 00886 "replace-registry", 00887 "Replace shared memory registry.", 00888 "", 00889 JackParamBool, 00890 &server_ptr->replace_registry, 00891 &server_ptr->default_replace_registry, 00892 value) == NULL) 00893 { 00894 goto fail_free_parameters; 00895 } 00896 00897 value.b = false; 00898 if (jackctl_add_parameter( 00899 &server_ptr->parameters, 00900 "sync", 00901 "Use server synchronous mode.", 00902 "", 00903 JackParamBool, 00904 &server_ptr->sync, 00905 &server_ptr->default_sync, 00906 value) == NULL) 00907 { 00908 goto fail_free_parameters; 00909 } 00910 00911 value.c = JACK_DEFAULT_SELF_CONNECT_MODE; 00912 if (jackctl_add_parameter( 00913 &server_ptr->parameters, 00914 "self-connect-mode", 00915 "Self connect mode.", 00916 "Whether JACK clients are allowed to connect their own ports", 00917 JackParamChar, 00918 &server_ptr->self_connect_mode, 00919 &server_ptr->default_self_connect_mode, 00920 value, 00921 jack_constraint_compose_enum_char( 00922 JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE, 00923 self_connect_mode_constraint_descr_array)) == NULL) 00924 { 00925 goto fail_free_parameters; 00926 } 00927 00928 JackServerGlobals::on_device_acquire = on_device_acquire; 00929 JackServerGlobals::on_device_release = on_device_release; 00930 00931 if (!jackctl_drivers_load(server_ptr)) 00932 { 00933 goto fail_free_parameters; 00934 } 00935 00936 /* Allowed to fail */ 00937 jackctl_internals_load(server_ptr); 00938 00939 return server_ptr; 00940 00941 fail_free_parameters: 00942 jackctl_server_free_parameters(server_ptr); 00943 00944 free(server_ptr); 00945 00946 fail: 00947 return NULL; 00948 } 00949 00950 SERVER_EXPORT void jackctl_server_destroy(jackctl_server *server_ptr) 00951 { 00952 if (server_ptr) { 00953 jackctl_server_free_drivers(server_ptr); 00954 jackctl_server_free_internals(server_ptr); 00955 jackctl_server_free_parameters(server_ptr); 00956 free(server_ptr); 00957 } 00958 } 00959 00960 SERVER_EXPORT const JSList * jackctl_server_get_drivers_list(jackctl_server *server_ptr) 00961 { 00962 return (server_ptr) ? server_ptr->drivers : NULL; 00963 } 00964 00965 SERVER_EXPORT bool jackctl_server_stop(jackctl_server *server_ptr) 00966 { 00967 if (server_ptr) { 00968 server_ptr->engine->Stop(); 00969 return true; 00970 } else { 00971 return false; 00972 } 00973 } 00974 00975 SERVER_EXPORT bool jackctl_server_close(jackctl_server *server_ptr) 00976 { 00977 if (server_ptr) { 00978 server_ptr->engine->Close(); 00979 delete server_ptr->engine; 00980 00981 /* clean up shared memory and files from this server instance */ 00982 jack_log("Cleaning up shared memory"); 00983 00984 jack_cleanup_shm(); 00985 00986 jack_log("Cleaning up files"); 00987 00988 JackTools::CleanupFiles(server_ptr->name.str); 00989 00990 jack_log("Unregistering server `%s'", server_ptr->name.str); 00991 00992 jack_unregister_server(server_ptr->name.str); 00993 00994 server_ptr->engine = NULL; 00995 00996 return true; 00997 } else { 00998 return false; 00999 } 01000 } 01001 01002 SERVER_EXPORT const JSList * jackctl_server_get_parameters(jackctl_server *server_ptr) 01003 { 01004 return (server_ptr) ? server_ptr->parameters : NULL; 01005 } 01006 01007 SERVER_EXPORT bool 01008 jackctl_server_open( 01009 jackctl_server *server_ptr, 01010 jackctl_driver *driver_ptr) 01011 { 01012 JSList * paramlist = NULL; 01013 01014 try { 01015 01016 if (!server_ptr || !driver_ptr) { 01017 return false; 01018 } 01019 01020 int rc = jack_register_server(server_ptr->name.str, server_ptr->replace_registry.b); 01021 switch (rc) 01022 { 01023 case EEXIST: 01024 jack_error("`%s' server already active", server_ptr->name.str); 01025 goto fail; 01026 case ENOSPC: 01027 jack_error("Too many servers already active"); 01028 goto fail; 01029 case ENOMEM: 01030 jack_error("No access to shm registry"); 01031 goto fail; 01032 } 01033 01034 jack_log("Server `%s' registered", server_ptr->name.str); 01035 01036 /* clean up shared memory and files from any previous 01037 * instance of this server name */ 01038 jack_cleanup_shm(); 01039 JackTools::CleanupFiles(server_ptr->name.str); 01040 01041 if (!server_ptr->realtime.b && server_ptr->client_timeout.i == 0) { 01042 server_ptr->client_timeout.i = 500; /* 0.5 sec; usable when non realtime. */ 01043 } 01044 01045 /* check port max value before allocating server */ 01046 if (server_ptr->port_max.ui > PORT_NUM_MAX) { 01047 jack_error("Jack server started with too much ports %d (when port max can be %d)", server_ptr->port_max.ui, PORT_NUM_MAX); 01048 goto fail; 01049 } 01050 01051 /* get the engine/driver started */ 01052 server_ptr->engine = new JackServer( 01053 server_ptr->sync.b, 01054 server_ptr->temporary.b, 01055 server_ptr->client_timeout.i, 01056 server_ptr->realtime.b, 01057 server_ptr->realtime_priority.i, 01058 server_ptr->port_max.ui, 01059 server_ptr->verbose.b, 01060 (jack_timer_type_t)server_ptr->clock_source.ui, 01061 server_ptr->self_connect_mode.c, 01062 server_ptr->name.str); 01063 if (server_ptr->engine == NULL) 01064 { 01065 jack_error("Failed to create new JackServer object"); 01066 goto fail_unregister; 01067 } 01068 01069 if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) goto fail_delete; 01070 rc = server_ptr->engine->Open(driver_ptr->desc_ptr, paramlist); 01071 jackctl_destroy_param_list(paramlist); 01072 if (rc < 0) 01073 { 01074 jack_error("JackServer::Open failed with %d", rc); 01075 goto fail_delete; 01076 } 01077 01078 return true; 01079 01080 } catch (std::exception e) { 01081 jack_error("jackctl_server_open error..."); 01082 jackctl_destroy_param_list(paramlist); 01083 } 01084 01085 fail_delete: 01086 delete server_ptr->engine; 01087 server_ptr->engine = NULL; 01088 01089 fail_unregister: 01090 jack_log("Cleaning up shared memory"); 01091 01092 jack_cleanup_shm(); 01093 01094 jack_log("Cleaning up files"); 01095 01096 JackTools::CleanupFiles(server_ptr->name.str); 01097 01098 jack_log("Unregistering server `%s'", server_ptr->name.str); 01099 01100 jack_unregister_server(server_ptr->name.str); 01101 01102 fail: 01103 return false; 01104 } 01105 01106 SERVER_EXPORT bool 01107 jackctl_server_start( 01108 jackctl_server *server_ptr) 01109 { 01110 if (!server_ptr) { 01111 return false; 01112 } else { 01113 int rc = server_ptr->engine->Start(); 01114 bool result = rc >= 0; 01115 if (! result) 01116 { 01117 jack_error("JackServer::Start() failed with %d", rc); 01118 } 01119 return result; 01120 } 01121 } 01122 01123 SERVER_EXPORT const char * jackctl_driver_get_name(jackctl_driver *driver_ptr) 01124 { 01125 return (driver_ptr) ? driver_ptr->desc_ptr->name : NULL; 01126 } 01127 01128 SERVER_EXPORT jackctl_driver_type_t jackctl_driver_get_type(jackctl_driver *driver_ptr) 01129 { 01130 return (driver_ptr) ? (jackctl_driver_type_t)driver_ptr->desc_ptr->type : (jackctl_driver_type_t)0; 01131 } 01132 01133 SERVER_EXPORT const JSList * jackctl_driver_get_parameters(jackctl_driver *driver_ptr) 01134 { 01135 return (driver_ptr) ? driver_ptr->parameters : NULL; 01136 } 01137 01138 SERVER_EXPORT jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver *driver_ptr) 01139 { 01140 return (driver_ptr) ? driver_ptr->desc_ptr : NULL; 01141 } 01142 01143 SERVER_EXPORT const char * jackctl_parameter_get_name(jackctl_parameter *parameter_ptr) 01144 { 01145 return (parameter_ptr) ? parameter_ptr->name : NULL; 01146 } 01147 01148 SERVER_EXPORT const char * jackctl_parameter_get_short_description(jackctl_parameter *parameter_ptr) 01149 { 01150 return (parameter_ptr) ? parameter_ptr->short_description : NULL; 01151 } 01152 01153 SERVER_EXPORT const char * jackctl_parameter_get_long_description(jackctl_parameter *parameter_ptr) 01154 { 01155 return (parameter_ptr) ? parameter_ptr->long_description : NULL; 01156 } 01157 01158 SERVER_EXPORT bool jackctl_parameter_has_range_constraint(jackctl_parameter *parameter_ptr) 01159 { 01160 return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) != 0) : false; 01161 } 01162 01163 SERVER_EXPORT bool jackctl_parameter_has_enum_constraint(jackctl_parameter *parameter_ptr) 01164 { 01165 return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_RANGE) == 0): false; 01166 } 01167 01168 SERVER_EXPORT uint32_t jackctl_parameter_get_enum_constraints_count(jackctl_parameter *parameter_ptr) 01169 { 01170 if (!parameter_ptr) { 01171 return 0; 01172 } 01173 01174 if (!jackctl_parameter_has_enum_constraint(parameter_ptr)) 01175 { 01176 return 0; 01177 } 01178 01179 return parameter_ptr->constraint_ptr->constraint.enumeration.count; 01180 } 01181 01182 SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value(jackctl_parameter *parameter_ptr, uint32_t index) 01183 { 01184 jack_driver_param_value_t * value_ptr; 01185 union jackctl_parameter_value jackctl_value; 01186 01187 if (!parameter_ptr) { 01188 memset(&jackctl_value, 0, sizeof(jackctl_value)); 01189 return jackctl_value; 01190 } 01191 01192 value_ptr = ¶meter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].value; 01193 01194 switch (parameter_ptr->type) 01195 { 01196 case JackParamInt: 01197 jackctl_value.i = value_ptr->i; 01198 break; 01199 case JackParamUInt: 01200 jackctl_value.ui = value_ptr->ui; 01201 break; 01202 case JackParamChar: 01203 jackctl_value.c = value_ptr->c; 01204 break; 01205 case JackParamString: 01206 strcpy(jackctl_value.str, value_ptr->str); 01207 break; 01208 default: 01209 jack_error("Bad driver parameter type %i (enum constraint)", (int)parameter_ptr->type); 01210 assert(0); 01211 } 01212 01213 return jackctl_value; 01214 } 01215 01216 SERVER_EXPORT const char * jackctl_parameter_get_enum_constraint_description(jackctl_parameter *parameter_ptr, uint32_t index) 01217 { 01218 return (parameter_ptr) ? parameter_ptr->constraint_ptr->constraint.enumeration.possible_values_array[index].short_desc : NULL; 01219 } 01220 01221 SERVER_EXPORT void jackctl_parameter_get_range_constraint(jackctl_parameter *parameter_ptr, union jackctl_parameter_value * min_ptr, union jackctl_parameter_value * max_ptr) 01222 { 01223 if (!parameter_ptr || !min_ptr || !max_ptr) { 01224 return; 01225 } 01226 01227 switch (parameter_ptr->type) 01228 { 01229 case JackParamInt: 01230 min_ptr->i = parameter_ptr->constraint_ptr->constraint.range.min.i; 01231 max_ptr->i = parameter_ptr->constraint_ptr->constraint.range.max.i; 01232 return; 01233 case JackParamUInt: 01234 min_ptr->ui = parameter_ptr->constraint_ptr->constraint.range.min.ui; 01235 max_ptr->ui = parameter_ptr->constraint_ptr->constraint.range.max.ui; 01236 return; 01237 default: 01238 jack_error("Bad driver parameter type %i (range constraint)", (int)parameter_ptr->type); 01239 assert(0); 01240 } 01241 } 01242 01243 SERVER_EXPORT bool jackctl_parameter_constraint_is_strict(jackctl_parameter_t * parameter_ptr) 01244 { 01245 return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_STRICT) != 0) : false; 01246 } 01247 01248 SERVER_EXPORT bool jackctl_parameter_constraint_is_fake_value(jackctl_parameter_t * parameter_ptr) 01249 { 01250 return (parameter_ptr) ? (parameter_ptr->constraint_ptr != NULL && (parameter_ptr->constraint_ptr->flags & JACK_CONSTRAINT_FLAG_FAKE_VALUE) != 0) : false; 01251 } 01252 01253 SERVER_EXPORT jackctl_param_type_t jackctl_parameter_get_type(jackctl_parameter *parameter_ptr) 01254 { 01255 return (parameter_ptr) ? parameter_ptr->type : (jackctl_param_type_t)0; 01256 } 01257 01258 SERVER_EXPORT char jackctl_parameter_get_id(jackctl_parameter_t * parameter_ptr) 01259 { 01260 return (parameter_ptr) ? parameter_ptr->id : 0; 01261 } 01262 01263 SERVER_EXPORT bool jackctl_parameter_is_set(jackctl_parameter *parameter_ptr) 01264 { 01265 return (parameter_ptr) ? parameter_ptr->is_set : false; 01266 } 01267 01268 SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_value(jackctl_parameter *parameter_ptr) 01269 { 01270 if (parameter_ptr) { 01271 return *parameter_ptr->value_ptr; 01272 } else { 01273 union jackctl_parameter_value jackctl_value; 01274 memset(&jackctl_value, 0, sizeof(jackctl_value)); 01275 return jackctl_value; 01276 } 01277 } 01278 01279 SERVER_EXPORT bool jackctl_parameter_reset(jackctl_parameter *parameter_ptr) 01280 { 01281 if (!parameter_ptr) { 01282 return NULL; 01283 } 01284 01285 if (!parameter_ptr->is_set) 01286 { 01287 return true; 01288 } 01289 01290 parameter_ptr->is_set = false; 01291 01292 *parameter_ptr->value_ptr = *parameter_ptr->default_value_ptr; 01293 01294 return true; 01295 } 01296 01297 SERVER_EXPORT bool jackctl_parameter_set_value(jackctl_parameter *parameter_ptr, const union jackctl_parameter_value * value_ptr) 01298 { 01299 if (!parameter_ptr || !value_ptr) { 01300 return NULL; 01301 } 01302 01303 parameter_ptr->is_set = true; 01304 *parameter_ptr->value_ptr = *value_ptr; 01305 01306 return true; 01307 } 01308 01309 SERVER_EXPORT union jackctl_parameter_value jackctl_parameter_get_default_value(jackctl_parameter *parameter_ptr) 01310 { 01311 if (parameter_ptr) { 01312 return *parameter_ptr->default_value_ptr; 01313 } else { 01314 union jackctl_parameter_value jackctl_value; 01315 memset(&jackctl_value, 0, sizeof(jackctl_value)); 01316 return jackctl_value; 01317 } 01318 } 01319 01320 // Internals clients 01321 01322 SERVER_EXPORT const JSList * jackctl_server_get_internals_list(jackctl_server *server_ptr) 01323 { 01324 return (server_ptr) ? server_ptr->internals : NULL; 01325 } 01326 01327 SERVER_EXPORT const char * jackctl_internal_get_name(jackctl_internal *internal_ptr) 01328 { 01329 return (internal_ptr) ? internal_ptr->desc_ptr->name : NULL; 01330 } 01331 01332 SERVER_EXPORT const JSList * jackctl_internal_get_parameters(jackctl_internal *internal_ptr) 01333 { 01334 return (internal_ptr) ? internal_ptr->parameters : NULL; 01335 } 01336 01337 SERVER_EXPORT bool jackctl_server_load_internal( 01338 jackctl_server * server_ptr, 01339 jackctl_internal * internal) 01340 { 01341 if (!server_ptr || !internal) { 01342 return false; 01343 } 01344 01345 int status; 01346 if (server_ptr->engine != NULL) { 01347 JSList * paramlist; 01348 if (!jackctl_create_param_list(internal->parameters, ¶mlist)) return false; 01349 server_ptr->engine->InternalClientLoad2(internal->desc_ptr->name, internal->desc_ptr->name, paramlist, JackNullOption, &internal->refnum, -1, &status); 01350 jackctl_destroy_param_list(paramlist); 01351 return (internal->refnum > 0); 01352 } else { 01353 return false; 01354 } 01355 } 01356 01357 SERVER_EXPORT bool jackctl_server_unload_internal( 01358 jackctl_server * server_ptr, 01359 jackctl_internal * internal) 01360 { 01361 if (!server_ptr || !internal) { 01362 return false; 01363 } 01364 01365 int status; 01366 if (server_ptr->engine != NULL && internal->refnum > 0) { 01367 // Client object is internally kept in JackEngine, and will be deallocated in InternalClientUnload 01368 return ((server_ptr->engine->GetEngine()->InternalClientUnload(internal->refnum, &status)) == 0); 01369 } else { 01370 return false; 01371 } 01372 } 01373 01374 SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) 01375 { 01376 if (server_ptr && server_ptr->engine) { 01377 if (server_ptr->engine->IsRunning()) { 01378 jack_error("Cannot add a slave in a running server"); 01379 return false; 01380 } else { 01381 JSList * paramlist; 01382 if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) return false; 01383 JackDriverInfo* info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, paramlist); 01384 jackctl_destroy_param_list(paramlist); 01385 if (info) { 01386 driver_ptr->infos = jack_slist_append(driver_ptr->infos, info); 01387 return true; 01388 } else { 01389 return false; 01390 } 01391 } 01392 } else { 01393 return false; 01394 } 01395 } 01396 01397 SERVER_EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) 01398 { 01399 if (server_ptr && server_ptr->engine) { 01400 if (server_ptr->engine->IsRunning()) { 01401 jack_error("Cannot remove a slave from a running server"); 01402 return false; 01403 } else { 01404 if (driver_ptr->infos) { 01405 JackDriverInfo* info = (JackDriverInfo*)driver_ptr->infos->data; 01406 assert(info); 01407 driver_ptr->infos = jack_slist_remove(driver_ptr->infos, info); 01408 server_ptr->engine->RemoveSlave(info); 01409 delete info; 01410 return true; 01411 } else { 01412 return false; 01413 } 01414 } 01415 } else { 01416 return false; 01417 } 01418 } 01419 01420 SERVER_EXPORT bool jackctl_server_switch_master(jackctl_server * server_ptr, jackctl_driver * driver_ptr) 01421 { 01422 if (server_ptr && server_ptr->engine) { 01423 JSList * paramlist; 01424 if (!jackctl_create_param_list(driver_ptr->parameters, ¶mlist)) return false; 01425 bool ret = (server_ptr->engine->SwitchMaster(driver_ptr->desc_ptr, paramlist) == 0); 01426 jackctl_destroy_param_list(paramlist); 01427 return ret; 01428 } else { 01429 return false; 01430 } 01431 } 01432 01433