00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120 #include "asterisk.h"
00121
00122 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 388195 $")
00123
00124 #include "asterisk/_private.h"
00125 #include "asterisk/channel.h"
00126 #include "asterisk/utils.h"
00127 #include "asterisk/lock.h"
00128 #include "asterisk/linkedlists.h"
00129 #include "asterisk/devicestate.h"
00130 #include "asterisk/pbx.h"
00131 #include "asterisk/app.h"
00132 #include "asterisk/event.h"
00133
00134
00135 static const char * const devstatestring[][2] = {
00136 { "Unknown", "UNKNOWN" },
00137 { "Not in use", "NOT_INUSE" },
00138 { "In use", "INUSE" },
00139 { "Busy", "BUSY" },
00140 { "Invalid", "INVALID" },
00141 { "Unavailable", "UNAVAILABLE" },
00142 { "Ringing", "RINGING" },
00143 { "Ring+Inuse", "RINGINUSE" },
00144 { "On Hold", "ONHOLD" },
00145 };
00146
00147
00148 static const struct chan2dev {
00149 enum ast_channel_state chan;
00150 enum ast_device_state dev;
00151 } chan2dev[] = {
00152 { AST_STATE_DOWN, AST_DEVICE_NOT_INUSE },
00153 { AST_STATE_RESERVED, AST_DEVICE_INUSE },
00154 { AST_STATE_OFFHOOK, AST_DEVICE_INUSE },
00155 { AST_STATE_DIALING, AST_DEVICE_INUSE },
00156 { AST_STATE_RING, AST_DEVICE_INUSE },
00157 { AST_STATE_RINGING, AST_DEVICE_RINGING },
00158 { AST_STATE_UP, AST_DEVICE_INUSE },
00159 { AST_STATE_BUSY, AST_DEVICE_BUSY },
00160 { AST_STATE_DIALING_OFFHOOK, AST_DEVICE_INUSE },
00161 { AST_STATE_PRERING, AST_DEVICE_RINGING },
00162 { -100, -100 },
00163 };
00164
00165
00166 struct devstate_prov {
00167 char label[40];
00168 ast_devstate_prov_cb_type callback;
00169 AST_RWLIST_ENTRY(devstate_prov) list;
00170 };
00171
00172
00173 static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
00174
00175 struct state_change {
00176 AST_LIST_ENTRY(state_change) list;
00177 enum ast_devstate_cache cachable;
00178 char device[1];
00179 };
00180
00181
00182
00183 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00184
00185
00186 static pthread_t change_thread = AST_PTHREADT_NULL;
00187
00188
00189 static ast_cond_t change_pending;
00190
00191 struct devstate_change {
00192 AST_LIST_ENTRY(devstate_change) entry;
00193 uint32_t state;
00194 struct ast_eid eid;
00195 enum ast_devstate_cache cachable;
00196 char device[1];
00197 };
00198
00199 static struct {
00200 pthread_t thread;
00201 struct ast_event_sub *event_sub;
00202 ast_cond_t cond;
00203 ast_mutex_t lock;
00204 AST_LIST_HEAD_NOLOCK(, devstate_change) devstate_change_q;
00205 unsigned int enabled:1;
00206 } devstate_collector = {
00207 .thread = AST_PTHREADT_NULL,
00208 .enabled = 0,
00209 };
00210
00211
00212 static int getproviderstate(const char *provider, const char *address);
00213
00214
00215 const char *ast_devstate2str(enum ast_device_state devstate)
00216 {
00217 return devstatestring[devstate][0];
00218 }
00219
00220
00221 const char *devstate2str(enum ast_device_state devstate)
00222 {
00223 return devstatestring[devstate][0];
00224 }
00225
00226 enum ast_device_state ast_state_chan2dev(enum ast_channel_state chanstate)
00227 {
00228 int i;
00229 chanstate &= 0xFFFF;
00230 for (i = 0; chan2dev[i].chan != -100; i++) {
00231 if (chan2dev[i].chan == chanstate) {
00232 return chan2dev[i].dev;
00233 }
00234 }
00235 return AST_DEVICE_UNKNOWN;
00236 }
00237
00238
00239 const char *ast_devstate_str(enum ast_device_state state)
00240 {
00241 return devstatestring[state][1];
00242 }
00243
00244 enum ast_device_state ast_devstate_val(const char *val)
00245 {
00246 if (!strcasecmp(val, "NOT_INUSE"))
00247 return AST_DEVICE_NOT_INUSE;
00248 else if (!strcasecmp(val, "INUSE"))
00249 return AST_DEVICE_INUSE;
00250 else if (!strcasecmp(val, "BUSY"))
00251 return AST_DEVICE_BUSY;
00252 else if (!strcasecmp(val, "INVALID"))
00253 return AST_DEVICE_INVALID;
00254 else if (!strcasecmp(val, "UNAVAILABLE"))
00255 return AST_DEVICE_UNAVAILABLE;
00256 else if (!strcasecmp(val, "RINGING"))
00257 return AST_DEVICE_RINGING;
00258 else if (!strcasecmp(val, "RINGINUSE"))
00259 return AST_DEVICE_RINGINUSE;
00260 else if (!strcasecmp(val, "ONHOLD"))
00261 return AST_DEVICE_ONHOLD;
00262
00263 return AST_DEVICE_UNKNOWN;
00264 }
00265
00266
00267
00268
00269
00270
00271 enum ast_device_state ast_parse_device_state(const char *device)
00272 {
00273 struct ast_channel *chan;
00274 char match[AST_CHANNEL_NAME];
00275 enum ast_device_state res;
00276
00277 snprintf(match, sizeof(match), "%s-", device);
00278
00279 if (!(chan = ast_channel_get_by_name_prefix(match, strlen(match)))) {
00280 return AST_DEVICE_UNKNOWN;
00281 }
00282
00283 res = (chan->_state == AST_STATE_RINGING) ? AST_DEVICE_RINGING : AST_DEVICE_INUSE;
00284
00285 chan = ast_channel_unref(chan);
00286
00287 return res;
00288 }
00289
00290 static enum ast_device_state devstate_cached(const char *device)
00291 {
00292 enum ast_device_state res = AST_DEVICE_UNKNOWN;
00293 struct ast_event *event;
00294
00295 event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
00296 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00297 AST_EVENT_IE_END);
00298
00299 if (!event)
00300 return res;
00301
00302 res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00303
00304 ast_event_destroy(event);
00305
00306 return res;
00307 }
00308
00309
00310 static enum ast_device_state _ast_device_state(const char *device, int check_cache)
00311 {
00312 char *buf;
00313 char *number;
00314 const struct ast_channel_tech *chan_tech;
00315 enum ast_device_state res;
00316
00317 char *tech;
00318
00319 char *provider = NULL;
00320
00321
00322 if (check_cache) {
00323 res = devstate_cached(device);
00324 if (res != AST_DEVICE_UNKNOWN) {
00325 return res;
00326 }
00327 }
00328
00329 buf = ast_strdupa(device);
00330 tech = strsep(&buf, "/");
00331 if (!(number = buf)) {
00332 provider = strsep(&tech, ":");
00333 if (!tech) {
00334 return AST_DEVICE_INVALID;
00335 }
00336
00337 number = tech;
00338 tech = NULL;
00339
00340 ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
00341 return getproviderstate(provider, number);
00342 }
00343
00344 ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
00345
00346 if (!(chan_tech = ast_get_channel_tech(tech)))
00347 return AST_DEVICE_INVALID;
00348
00349 if (!(chan_tech->devicestate))
00350 return ast_parse_device_state(device);
00351
00352 res = chan_tech->devicestate(number);
00353
00354 if (res != AST_DEVICE_UNKNOWN)
00355 return res;
00356
00357 res = ast_parse_device_state(device);
00358
00359 return res;
00360 }
00361
00362 enum ast_device_state ast_device_state(const char *device)
00363 {
00364
00365
00366
00367 return _ast_device_state(device, 1);
00368 }
00369
00370
00371 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
00372 {
00373 struct devstate_prov *devprov;
00374
00375 if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
00376 return -1;
00377
00378 devprov->callback = callback;
00379 ast_copy_string(devprov->label, label, sizeof(devprov->label));
00380
00381 AST_RWLIST_WRLOCK(&devstate_provs);
00382 AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
00383 AST_RWLIST_UNLOCK(&devstate_provs);
00384
00385 return 0;
00386 }
00387
00388
00389 int ast_devstate_prov_del(const char *label)
00390 {
00391 struct devstate_prov *devcb;
00392 int res = -1;
00393
00394 AST_RWLIST_WRLOCK(&devstate_provs);
00395 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
00396 if (!strcasecmp(devcb->label, label)) {
00397 AST_RWLIST_REMOVE_CURRENT(list);
00398 ast_free(devcb);
00399 res = 0;
00400 break;
00401 }
00402 }
00403 AST_RWLIST_TRAVERSE_SAFE_END;
00404 AST_RWLIST_UNLOCK(&devstate_provs);
00405
00406 return res;
00407 }
00408
00409
00410 static int getproviderstate(const char *provider, const char *address)
00411 {
00412 struct devstate_prov *devprov;
00413 int res = AST_DEVICE_INVALID;
00414
00415 AST_RWLIST_RDLOCK(&devstate_provs);
00416 AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
00417 ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
00418
00419 if (!strcasecmp(devprov->label, provider)) {
00420 res = devprov->callback(address);
00421 break;
00422 }
00423 }
00424 AST_RWLIST_UNLOCK(&devstate_provs);
00425
00426 return res;
00427 }
00428
00429 static void devstate_event(const char *device, enum ast_device_state state, int cachable)
00430 {
00431 struct ast_event *event;
00432 enum ast_event_type event_type;
00433
00434 if (devstate_collector.enabled) {
00435
00436
00437 event_type = AST_EVENT_DEVICE_STATE_CHANGE;
00438 } else {
00439 event_type = AST_EVENT_DEVICE_STATE;
00440 }
00441
00442 ast_debug(3, "device '%s' state '%d'\n", device, state);
00443
00444 if (!(event = ast_event_new(event_type,
00445 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00446 AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00447 AST_EVENT_IE_CACHABLE, AST_EVENT_IE_PLTYPE_UINT, cachable,
00448 AST_EVENT_IE_END))) {
00449 return;
00450 }
00451
00452 if (cachable) {
00453 ast_event_queue_and_cache(event);
00454 } else {
00455 ast_event_queue(event);
00456 }
00457 }
00458
00459
00460
00461 static void do_state_change(const char *device, int cachable)
00462 {
00463 enum ast_device_state state;
00464
00465 state = _ast_device_state(device, 0);
00466
00467 ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, ast_devstate2str(state));
00468
00469 devstate_event(device, state, cachable);
00470 }
00471
00472 int ast_devstate_changed_literal(enum ast_device_state state, enum ast_devstate_cache cachable, const char *device)
00473 {
00474 struct state_change *change;
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 if (state != AST_DEVICE_UNKNOWN) {
00493 devstate_event(device, state, cachable);
00494 } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
00495
00496
00497 do_state_change(device, cachable);
00498 } else {
00499
00500 strcpy(change->device, device);
00501 change->cachable = cachable;
00502 AST_LIST_LOCK(&state_changes);
00503 AST_LIST_INSERT_TAIL(&state_changes, change, list);
00504 ast_cond_signal(&change_pending);
00505 AST_LIST_UNLOCK(&state_changes);
00506 }
00507
00508 return 1;
00509 }
00510
00511 int ast_device_state_changed_literal(const char *dev)
00512 {
00513 return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, dev);
00514 }
00515
00516 int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt, ...)
00517 {
00518 char buf[AST_MAX_EXTENSION];
00519 va_list ap;
00520
00521 va_start(ap, fmt);
00522 vsnprintf(buf, sizeof(buf), fmt, ap);
00523 va_end(ap);
00524
00525 return ast_devstate_changed_literal(state, cachable, buf);
00526 }
00527
00528 int ast_device_state_changed(const char *fmt, ...)
00529 {
00530 char buf[AST_MAX_EXTENSION];
00531 va_list ap;
00532
00533 va_start(ap, fmt);
00534 vsnprintf(buf, sizeof(buf), fmt, ap);
00535 va_end(ap);
00536
00537 return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, buf);
00538 }
00539
00540
00541 static void *do_devstate_changes(void *data)
00542 {
00543 struct state_change *next, *current;
00544
00545 for (;;) {
00546
00547 AST_LIST_LOCK(&state_changes);
00548 if (AST_LIST_EMPTY(&state_changes))
00549 ast_cond_wait(&change_pending, &state_changes.lock);
00550 next = AST_LIST_FIRST(&state_changes);
00551 AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
00552 AST_LIST_UNLOCK(&state_changes);
00553
00554
00555 while ((current = next)) {
00556 next = AST_LIST_NEXT(current, list);
00557 do_state_change(current->device, current->cachable);
00558 ast_free(current);
00559 }
00560 }
00561
00562 return NULL;
00563 }
00564
00565 static void destroy_devstate_change(struct devstate_change *sc)
00566 {
00567 ast_free(sc);
00568 }
00569
00570 #define MAX_SERVERS 64
00571 struct change_collection {
00572 struct devstate_change states[MAX_SERVERS];
00573 size_t num_states;
00574 };
00575
00576 static void devstate_cache_cb(const struct ast_event *event, void *data)
00577 {
00578 struct change_collection *collection = data;
00579 int i;
00580 const struct ast_eid *eid;
00581
00582 if (collection->num_states == ARRAY_LEN(collection->states)) {
00583 ast_log(LOG_ERROR, "More per-server state values than we have room for (MAX_SERVERS is %d)\n",
00584 MAX_SERVERS);
00585 return;
00586 }
00587
00588 if (!(eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
00589 ast_log(LOG_ERROR, "Device state change event with no EID\n");
00590 return;
00591 }
00592
00593 i = collection->num_states;
00594
00595 collection->states[i].state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00596 collection->states[i].eid = *eid;
00597
00598 collection->num_states++;
00599 }
00600
00601 static void process_collection(const char *device, enum ast_devstate_cache cachable, struct change_collection *collection)
00602 {
00603 int i;
00604 struct ast_devstate_aggregate agg;
00605 enum ast_device_state state;
00606 struct ast_event *event;
00607
00608 ast_devstate_aggregate_init(&agg);
00609
00610 for (i = 0; i < collection->num_states; i++) {
00611 ast_debug(1, "Adding per-server state of '%s' for '%s'\n",
00612 ast_devstate2str(collection->states[i].state), device);
00613 ast_devstate_aggregate_add(&agg, collection->states[i].state);
00614 }
00615
00616 state = ast_devstate_aggregate_result(&agg);
00617
00618 ast_debug(1, "Aggregate devstate result is '%s' for '%s'\n",
00619 ast_devstate2str(state), device);
00620
00621 event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
00622 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00623 AST_EVENT_IE_END);
00624
00625 if (event) {
00626 enum ast_device_state old_state;
00627
00628 old_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00629
00630 ast_event_destroy(event);
00631
00632 if (state == old_state) {
00633
00634 ast_debug(1, "Aggregate state for device '%s' has not changed from '%s'\n",
00635 device, ast_devstate2str(state));
00636 return;
00637 }
00638 }
00639
00640 ast_debug(1, "Aggregate state for device '%s' has changed to '%s'\n",
00641 device, ast_devstate2str(state));
00642
00643 event = ast_event_new(AST_EVENT_DEVICE_STATE,
00644 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
00645 AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00646 AST_EVENT_IE_END);
00647
00648 if (!event) {
00649 return;
00650 }
00651
00652 if (cachable) {
00653 ast_event_queue_and_cache(event);
00654 } else {
00655 ast_event_queue(event);
00656 }
00657 }
00658
00659 static void handle_devstate_change(struct devstate_change *sc)
00660 {
00661 struct ast_event_sub *tmp_sub;
00662 struct change_collection collection = {
00663 .num_states = 0,
00664 };
00665
00666 ast_debug(1, "Processing device state change for '%s'\n", sc->device);
00667
00668 if (!(tmp_sub = ast_event_subscribe_new(AST_EVENT_DEVICE_STATE_CHANGE, devstate_cache_cb, &collection))) {
00669 ast_log(LOG_ERROR, "Failed to create subscription\n");
00670 return;
00671 }
00672
00673 if (ast_event_sub_append_ie_str(tmp_sub, AST_EVENT_IE_DEVICE, sc->device)) {
00674 ast_log(LOG_ERROR, "Failed to append device IE\n");
00675 ast_event_sub_destroy(tmp_sub);
00676 return;
00677 }
00678
00679
00680 ast_event_dump_cache(tmp_sub);
00681
00682 process_collection(sc->device, sc->cachable, &collection);
00683
00684 ast_event_sub_destroy(tmp_sub);
00685 }
00686
00687 static void *run_devstate_collector(void *data)
00688 {
00689 for (;;) {
00690 struct devstate_change *sc;
00691
00692 ast_mutex_lock(&devstate_collector.lock);
00693 while (!(sc = AST_LIST_REMOVE_HEAD(&devstate_collector.devstate_change_q, entry)))
00694 ast_cond_wait(&devstate_collector.cond, &devstate_collector.lock);
00695 ast_mutex_unlock(&devstate_collector.lock);
00696
00697 handle_devstate_change(sc);
00698
00699 destroy_devstate_change(sc);
00700 }
00701
00702 return NULL;
00703 }
00704
00705 static void devstate_change_collector_cb(const struct ast_event *event, void *data)
00706 {
00707 struct devstate_change *sc;
00708 const char *device, *cachable_str;
00709 const struct ast_eid *eid;
00710 uint32_t state;
00711 enum ast_devstate_cache cachable = AST_DEVSTATE_CACHABLE;
00712
00713 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00714 eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
00715 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00716
00717 if (ast_strlen_zero(device) || !eid) {
00718 ast_log(LOG_ERROR, "Invalid device state change event received\n");
00719 return;
00720 }
00721
00722 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(device))))
00723 return;
00724
00725 strcpy(sc->device, device);
00726 sc->eid = *eid;
00727 sc->state = state;
00728
00729
00730
00731
00732
00733
00734
00735 if ((cachable_str = ast_event_get_ie_str(event, AST_EVENT_IE_CACHABLE))) {
00736 sscanf(cachable_str, "%30u", &cachable);
00737 }
00738 sc->cachable = cachable;
00739
00740 ast_mutex_lock(&devstate_collector.lock);
00741 AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry);
00742 ast_cond_signal(&devstate_collector.cond);
00743 ast_mutex_unlock(&devstate_collector.lock);
00744 }
00745
00746
00747 int ast_device_state_engine_init(void)
00748 {
00749 ast_cond_init(&change_pending, NULL);
00750 if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
00751 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
00752 return -1;
00753 }
00754
00755 return 0;
00756 }
00757
00758 void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
00759 {
00760 memset(agg, 0, sizeof(*agg));
00761 agg->state = AST_DEVICE_INVALID;
00762 }
00763
00764 void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
00765 {
00766 static enum ast_device_state state_order[] = {
00767 1,
00768 3,
00769 6,
00770 7,
00771 0,
00772 2,
00773 5,
00774 8,
00775 4,
00776 };
00777
00778 if (state == AST_DEVICE_RINGING) {
00779 agg->ringing = 1;
00780 } else if (state == AST_DEVICE_INUSE || state == AST_DEVICE_ONHOLD || state == AST_DEVICE_BUSY) {
00781 agg->inuse = 1;
00782 }
00783
00784 if (agg->ringing && agg->inuse) {
00785 agg->state = AST_DEVICE_RINGINUSE;
00786 } else if (state_order[state] > state_order[agg->state]) {
00787 agg->state = state;
00788 }
00789 }
00790
00791 enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
00792 {
00793 return agg->state;
00794 }
00795
00796 int ast_enable_distributed_devstate(void)
00797 {
00798 if (devstate_collector.enabled) {
00799 return 0;
00800 }
00801
00802 devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
00803 devstate_change_collector_cb, "devicestate_engine_enable_distributed", NULL, AST_EVENT_IE_END);
00804
00805 if (!devstate_collector.event_sub) {
00806 ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
00807 return -1;
00808 }
00809
00810 ast_mutex_init(&devstate_collector.lock);
00811 ast_cond_init(&devstate_collector.cond, NULL);
00812 if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) {
00813 ast_log(LOG_ERROR, "Unable to start device state collector thread.\n");
00814 return -1;
00815 }
00816
00817 devstate_collector.enabled = 1;
00818
00819 return 0;
00820 }