event.c 30 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2 3 4 5 6
/*
 * EMULAB-COPYRIGHT
 * Copyright (c) 2000-2002 University of Utah and the Flux Group.
 * All rights reserved.
 */

Ian Murdock's avatar
Ian Murdock committed
7 8 9 10 11 12 13 14 15 16 17 18
/*
 * event.c --
 *
 *      Testbed event library.  Currently uses the Elvin publish/
 *      subscribe system for routing event notifications.
 */

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
19 20 21
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
22
#include "event.h"
23 24 25 26 27 28
#include "log.h"

#define ERROR(fmt,...) error(__FUNCTION__ ": " fmt, ## __VA_ARGS__)
#ifdef  DEBUG
#define TRACE(fmt,...) info(__FUNCTION__ ": " fmt, ## __VA_ARGS__)
#else
29
#define TRACE(fmt,...)
30
#endif
Ian Murdock's avatar
Ian Murdock committed
31 32

static char hostname[MAXHOSTNAMELEN];
33
static char ipaddr[32];
Ian Murdock's avatar
Ian Murdock committed
34

35 36 37 38 39 40
/*
 * Register with the testbed event system.  NAME specifies the name of
 * the event server.  Returns a pointer to a handle that may be passed
 * to other event system routines if the operation is successful, NULL
 * otherwise.
 *
Ian Murdock's avatar
Ian Murdock committed
41 42 43 44 45 46 47 48 49
 * The THREADED parameter should be set to 1 if the registering
 * client is multi-threaded. If THREADED is 1, the event
 * library will call routines that are thread-safe, and event
 * notifications will be dispatched using background threads (i.e.,
 * the client will supply its own event loop). If THREADED is 0, event
 * notifications will be dispatched using an event system-provided
 * event loop, and the client must call event_main after connecting in
 * order to receive notifications.
 *
50 51 52 53 54 55 56 57 58 59 60
 * Elvin note: NAME is a URL of the form "elvin:/[protocol
 * stack]/[endpoint]", where a protocol stack names a transport
 * module, a security module, and a marshaling module as a comma
 * separated list (e.g., "http,none,xml"), and the endpoint format
 * is dependent on the transport module used.  If no protocol
 * stack is given, the default stack (tcp, none, xdr) is used.  For the
 * testbed's purposes, "elvin://HOSTNAME" should suffice.  If NAME
 * is NULL, then Elvin's server discovery protocol will be used to find
 * the Elvin server.
 */

Ian Murdock's avatar
Ian Murdock committed
61
event_handle_t
62
event_register(char *name, int threaded)
Ian Murdock's avatar
Ian Murdock committed
63
{
64 65 66 67 68
    event_handle_t	handle;
    elvin_handle_t	server;
    elvin_error_t	status;
    struct hostent     *he;
    struct in_addr	myip;
Ian Murdock's avatar
Ian Murdock committed
69 70 71 72 73 74

    if (gethostname(hostname, MAXHOSTNAMELEN) == -1) {
        ERROR("could not get hostname: %s\n", strerror(errno));
        return 0;
    }

75 76 77 78 79 80 81 82 83 84
    /*
     * Get our IP address. Thats how we name ourselves to the
     * Testbed Event System. 
     */
    if (! (he = gethostbyname(hostname))) {
	ERROR("could not get IP address from hostname: %s", hostname);
    }
    memcpy((char *)&myip, he->h_addr, he->h_length);
    strcpy(ipaddr, inet_ntoa(myip));

Ian Murdock's avatar
Ian Murdock committed
85
    TRACE("registering with event system (hostname=\"%s\")\n", hostname);
Ian Murdock's avatar
Ian Murdock committed
86

87 88
    /* Allocate a handle to be returned to the caller: */
    handle = xmalloc(sizeof(*handle));
Ian Murdock's avatar
Ian Murdock committed
89

90 91
    /* Set up the Elvin interface pointers: */
    if (threaded) {
92
#ifdef THREADED
93 94 95 96 97 98 99
        handle->init = elvin_threaded_init_default;
        handle->connect = elvin_threaded_connect;
        handle->disconnect = elvin_threaded_disconnect;
        handle->cleanup = elvin_threaded_cleanup;
        handle->mainloop = NULL; /* no mainloop for mt programs */
        handle->notify = elvin_threaded_notify;
        handle->subscribe = elvin_threaded_add_subscription;
100 101 102 103 104
#else
	ERROR("Threaded API not linked in with the program!\n");
	free(handle);
	return 0;
#endif
105 106 107 108 109 110 111 112 113 114 115 116 117
    } else {
        handle->init = elvin_sync_init_default;
        handle->connect = elvin_sync_connect;
        handle->disconnect = elvin_sync_disconnect;
        handle->cleanup = elvin_sync_cleanup;
        handle->mainloop = elvin_sync_default_mainloop;
        handle->notify = elvin_sync_notify;
        handle->subscribe = elvin_sync_add_subscription;
    }

    /* Initialize the elvin interface: */

    status = handle->init();
Ian Murdock's avatar
Ian Murdock committed
118
    if (status == NULL) {
119 120
        ERROR("could not initialize Elvin\n");
        free(handle);
Ian Murdock's avatar
Ian Murdock committed
121 122 123 124 125 126
        return 0;
    }
    server = elvin_handle_alloc(status);
    if (server == NULL) {
        ERROR("elvin_handle_alloc failed: ");
        elvin_error_fprintf(stderr, status);
127
        free(handle);
Ian Murdock's avatar
Ian Murdock committed
128 129 130 131 132 133 134 135
        return 0;
    }

    /* Set the discovery scope to "testbed", so that we only interact
       with testbed elvin servers. */
    if (elvin_handle_set_discovery_scope(server, "testbed", status) == 0) {
        ERROR("elvin_handle_set_discovery_scope failed: ");
        elvin_error_fprintf(stderr, status);
136
        free(handle);
Ian Murdock's avatar
Ian Murdock committed
137 138 139
        return 0;
    }

140
    /* Set the server URL, if we were passed one by the user. */
141 142
    if (name) {
        if (elvin_handle_append_url(server, name, status) == 0) {
143 144
            ERROR("elvin_handle_append_url failed: ");
            elvin_error_fprintf(stderr, status);
145
            free(handle);
146 147 148 149
            return 0;
        }
    }

Ian Murdock's avatar
Ian Murdock committed
150
    /* Connect to the elvin server: */
151 152
    if (handle->connect(server, status) == 0) {
        ERROR("could not connect to Elvin server: ");
Ian Murdock's avatar
Ian Murdock committed
153
        elvin_error_fprintf(stderr, status);
154
        free(handle);
Ian Murdock's avatar
Ian Murdock committed
155 156 157 158 159 160 161 162 163
        return 0;
    }

    handle->server = server;
    handle->status = status;

    return handle;
}

164 165

/*
Ian Murdock's avatar
Ian Murdock committed
166
 * Unregister with the testbed event system. Returns non-zero if the
167 168 169
 * operation is successful, 0 otherwise.
 */

Ian Murdock's avatar
Ian Murdock committed
170 171 172 173
int
event_unregister(event_handle_t handle)
{
    if (!handle) {
Ian Murdock's avatar
Ian Murdock committed
174
        ERROR("invalid parameter\n");
Ian Murdock's avatar
Ian Murdock committed
175 176 177
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
178
    TRACE("unregistering with event system (hostname=\"%s\")\n", hostname);
Ian Murdock's avatar
Ian Murdock committed
179 180

    /* Disconnect from the elvin server: */
181 182
    if (handle->disconnect(handle->server, handle->status) == 0) {
        ERROR("could not disconnect from Elvin server: ");
Ian Murdock's avatar
Ian Murdock committed
183 184 185 186 187 188 189 190 191 192 193
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    /* Clean up: */

    if (elvin_handle_free(handle->server, handle->status) == 0) {
        ERROR("elvin_handle_free failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }
194 195
    if (handle->cleanup(1, handle->status) == 0) {
        ERROR("could not clean up Elvin state: ");
Ian Murdock's avatar
Ian Murdock committed
196 197 198 199 200 201 202 203 204
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    free(handle);

    return 1;
}

205

Mike Hibler's avatar
Mike Hibler committed
206
/*
207 208
 * An internal function to handle the two different event_poll calls, without
 * making the library user mess around with arguments they don't care about.
Mike Hibler's avatar
Mike Hibler committed
209 210 211
 */

int
212
internal_event_poll(event_handle_t handle, int blocking, unsigned int timeout)
Mike Hibler's avatar
Mike Hibler committed
213 214 215
{
	extern int depth;
	int rv;
216
	elvin_timeout_t elvin_timeout = NULL;
Mike Hibler's avatar
Mike Hibler committed
217 218 219 220 221 222

	if (!handle->mainloop) {
		ERROR("multithreaded programs cannot use event_poll\n");
		return 0;
	}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	/*
	 * If the user wants a timeout, set up an elvin timeout now. We just
	 * use a NULL callback, so that it simply causes a timeout, and doesn't
	 * actually do anything.
	 */
	if (timeout) {
		elvin_timeout = elvin_sync_add_timeout(NULL, timeout, NULL,
				NULL, handle->status);
		if (!elvin_timeout) {
			ERROR("Elvin elvin_sync_add_timeout failed\n");
			elvin_error_fprintf(stderr, handle->status);
			return elvin_error_get_code(handle->status);
		}
	}

Mike Hibler's avatar
Mike Hibler committed
238
	depth++;
239
	rv = elvin_sync_default_select_and_dispatch(blocking, handle->status);
Mike Hibler's avatar
Mike Hibler committed
240 241 242 243 244 245
	depth--;
	if (rv == 0) {
		ERROR("Elvin select_and_dispatch failed\n");
		elvin_error_fprintf(stderr, handle->status);
	}

246 247 248 249 250 251 252 253 254 255 256
	/*
	 * Try to remove the timeout - if it didn't go off, we don't want to
	 * hit it later. We don't check the return value, since, if it did go
	 * off (and we don't really have a good way of knowing that), it's not
	 * there any more, so it looks like an error.
	 */
	if (timeout && elvin_timeout) {
		elvin_error_t error;
		elvin_sync_remove_timeout(elvin_timeout, error);
	}

Mike Hibler's avatar
Mike Hibler committed
257 258 259
	return elvin_error_get_code(handle->status);
}

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
/*
 * A non-blocking poll of the event system
 */

int
event_poll(event_handle_t handle)
{
	return internal_event_poll(handle,0,0);
}

/*
 * A blocking poll of the event system, with an optional timeout
 */

int event_poll_blocking(event_handle_t handle, unsigned int timeout)
{
	return internal_event_poll(handle,1,timeout);
}
Mike Hibler's avatar
Mike Hibler committed
278

279 280 281 282 283 284
/*
 * Enter the main loop of the event system, waiting to receive event
 * notifications. Returns non-zero if the operation is successful, 0
 * otherwise.
 */

Ian Murdock's avatar
Ian Murdock committed
285 286 287 288 289 290
int
event_main(event_handle_t handle)
{
    int loop = 1;

    if (!handle) {
Ian Murdock's avatar
Ian Murdock committed
291
        ERROR("invalid parameter\n"); 
Ian Murdock's avatar
Ian Murdock committed
292 293 294
        return 0;
    }

295 296 297 298 299
    if (!handle->mainloop) {
        ERROR("multithreaded programs don't need to call event_main\n");
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
300 301
    TRACE("entering event loop...\n");

302 303
    if (handle->mainloop(&loop, handle->status) == 0) {
        ERROR("Elvin mainloop failed: ");
Ian Murdock's avatar
Ian Murdock committed
304 305 306 307 308 309 310
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}

311 312 313 314

/*
 * Send the event notification NOTIFICATION.  NOTIFICATION is
 * allocated by event_notification_alloc, and may optionally
315
 * have attributes added to it by event_notification_put_*.
316
 * Returns non-zero if the operation is successful, 0 otherwise.
Ian Murdock's avatar
Ian Murdock committed
317 318 319 320
 *
 * Note that NOTIFICATION is not deallocated by event_notify.  The
 * caller is responsible for deallocating the notification when it
 * is finished with it.
321 322
 */

Ian Murdock's avatar
Ian Murdock committed
323 324 325
int
event_notify(event_handle_t handle, event_notification_t notification)
{
Ian Murdock's avatar
Ian Murdock committed
326 327
    if (!handle || !notification) {
        ERROR("invalid parameter\n");
Ian Murdock's avatar
Ian Murdock committed
328 329 330
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
331
    TRACE("sending event notification %p\n", notification);
Ian Murdock's avatar
Ian Murdock committed
332 333

    /* Send notification to Elvin server for routing: */
334
    if (handle->notify(handle->server, notification, 1, NULL, handle->status)
Ian Murdock's avatar
Ian Murdock committed
335 336
        == 0)
    {
337
        ERROR("could not send event notification: ");
Ian Murdock's avatar
Ian Murdock committed
338 339 340 341 342 343 344
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}

345

346 347 348 349 350 351
/*
 * Schedule the event notification NOTIFICATION to be sent at time
 * TIME.  NOTIFICATION is allocated by event_notification_alloc,
 * and may optionally have attributes added to it by
 * event_notification_put_*.  Returns non-zero if the operation
 * is successful, 0 otherwise.
Ian Murdock's avatar
Ian Murdock committed
352 353 354 355
 *
 * This function essentially operates as a deferred event_notify.
 * event_notify sends notifications immediately,
 * whereas event_schedule sends notifications at some later time.
Ian Murdock's avatar
Ian Murdock committed
356 357 358 359
 *
 * Note that NOTIFICATION is not deallocated by event_schedule.
 * The caller is responsible for deallocating the notification
 * when it is finished with it.
360 361 362 363 364 365
 */

int
event_schedule(event_handle_t handle, event_notification_t notification,
               struct timeval *time)
{
Ian Murdock's avatar
Ian Murdock committed
366 367
    if (!handle || !notification || !time) {
        ERROR("invalid parameter\n");
368 369 370
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
371 372
    TRACE("scheduling event notification %p to be sent at time (%ld, %ld)\n",
          notification, time->tv_sec, time->tv_usec);
373

374 375 376 377 378 379
    /*
     * Add an attribute that signifies its a scheduler operation.
     */
    if (! event_notification_remove(handle, notification, "SCHEDULER") ||
	! event_notification_put_int32(handle, notification, "SCHEDULER", 1)) {
	ERROR("could not add scheduler attribute to notification %p\n",
Ian Murdock's avatar
Ian Murdock committed
380
              notification);
381 382 383 384 385 386 387 388 389 390
        return 0;
    }

    /* Add the time this event should be fired to the notification
       structure: */

    if (event_notification_put_int32(handle, notification, "time_sec",
                                     time->tv_sec)
        == 0)
    {
Ian Murdock's avatar
Ian Murdock committed
391 392
        ERROR("could not add time.tv_sec attribute to notification %p\n",
              notification);
393 394 395 396 397 398 399
        return 0;
    }
    if (event_notification_put_int32(handle, notification, "time_usec",
                                     time->tv_usec)
        == 0)

    {
Ian Murdock's avatar
Ian Murdock committed
400 401
        ERROR("could not add time.tv_usec attribute to notification %p\n",
              notification);
402 403 404 405 406 407 408 409
        return 0;
    }

    /* Send the event notification: */
    return event_notify(handle, notification);
}


410
/*
411 412 413
 * Allocate an event notification.  The address TUPLE defines who
 * should receive the notification. Returns a pointer to an event
 * notification structure if the operation is successful, 0 otherwise.
414 415
 */

Ian Murdock's avatar
Ian Murdock committed
416
event_notification_t
417
event_notification_alloc(event_handle_t handle, address_tuple_t tuple)
Ian Murdock's avatar
Ian Murdock committed
418 419 420
{
    elvin_notification_t notification;

421
    if (!handle || !tuple) {
Ian Murdock's avatar
Ian Murdock committed
422
        ERROR("invalid paramater\n");
Ian Murdock's avatar
Ian Murdock committed
423 424 425
        return NULL;
    }

426
    TRACE("allocating notification (tuple=%p)\n", tuple);
Ian Murdock's avatar
Ian Murdock committed
427 428 429 430 431 432 433 434

    notification = elvin_notification_alloc(handle->status);
    if (notification == NULL) {
        ERROR("elvin_notification_alloc failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return NULL;
    }

Ian Murdock's avatar
Ian Murdock committed
435
    TRACE("allocated notification %p\n", notification);
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
#define EVPUT(name, field) \
({ \
	char *foo = (tuple->field ? tuple->field : ADDRESSTUPLE_ALL); \
	\
	event_notification_put_string(handle, notification, name, foo); \
})
    
    /* Add the target address stuff to the notification */
    if (!EVPUT("SITE", site) ||
	!EVPUT("EXPT", expt) ||
	!EVPUT("GROUP", group) ||
	!EVPUT("HOST", host) ||
	!EVPUT("OBJTYPE", objtype) ||
	!EVPUT("OBJNAME", objname) ||
	!EVPUT("EVENTTYPE", eventtype) ||
	! event_notification_put_int32(handle, notification, "SCHEDULER", 0)) {
	ERROR("could not add attributes to notification %p\n", notification);
Ian Murdock's avatar
Ian Murdock committed
453 454 455
        return NULL;
    }

456 457 458
    /* Add our address */
    event_notification_set_sender(handle, notification, ipaddr);

Ian Murdock's avatar
Ian Murdock committed
459 460 461
    return notification;
}

462 463

/*
Ian Murdock's avatar
Ian Murdock committed
464
 * Free the event notification NOTIFICATION. Returns non-zero if the
465 466 467
 * operation is successful, 0 otherwise.
 */

Ian Murdock's avatar
Ian Murdock committed
468 469 470 471
int
event_notification_free(event_handle_t handle,
                        event_notification_t notification)
{
Ian Murdock's avatar
Ian Murdock committed
472 473
    if (!handle || !notification) {
        ERROR("invalid parameter\n");
Ian Murdock's avatar
Ian Murdock committed
474 475 476
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
477
    TRACE("freeing notification %p\n", notification);
Ian Murdock's avatar
Ian Murdock committed
478 479 480 481 482 483 484 485 486 487

    if (elvin_notification_free(notification, handle->status) == 0) {
        ERROR("elvin_notification_free failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
/*
 * Clones (copies) the event notificaion. Returns the copy if successful,
 * or NULL if it is not.
 */
event_notification_t
event_notification_clone(event_handle_t handle,
			 event_notification_t notification)
{
    event_notification_t clone;

    if (!handle || !notification) {
        ERROR("invalid parameter\n");
        return 0;
    }

    TRACE("cloning notification %p\n", notification);

    if (! (clone = elvin_notification_clone(notification, handle->status)) ) {
        ERROR("elvin_notification_clone failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return clone;

}

515

Ian Murdock's avatar
Ian Murdock committed
516 517
struct attr_traverse_arg {
    char *name;
518
    elvin_value_t *value;
Ian Murdock's avatar
Ian Murdock committed
519 520 521 522 523
};

static int attr_traverse(void *rock, char *name, elvin_basetypes_t type,
                         elvin_value_t value, elvin_error_t error);

524
/*
Ian Murdock's avatar
Ian Murdock committed
525 526
 * Get the attribute with name NAME from the event notification
 * NOTIFICATION.
527 528 529 530
 * Writes the value of the attribute to *VALUE and returns
 * non-zero if the named attribute is found, 0 otherwise.
 */

531 532 533 534
static int
event_notification_get(event_handle_t handle,
                       event_notification_t notification,
                       char *name, elvin_value_t *value)
Ian Murdock's avatar
Ian Murdock committed
535 536 537
{
    struct attr_traverse_arg arg;

Ian Murdock's avatar
Ian Murdock committed
538 539
    if (!handle || !notification || !name || !value) {
        ERROR("invalid parameter\n");
Ian Murdock's avatar
Ian Murdock committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
        return 0;
    }

    arg.name = name;
    arg.value = value;

    /* attr_traverse returns 0 to indicate that it has found the
       desired attribute. */
    if (elvin_notification_traverse(notification, attr_traverse, &arg,
                                    handle->status)
        == 0)
    {
        /* Found it. */
        return 1;
    }

    /* Didn't find it. */
    return 0;
}

560 561

/*
562 563 564 565
 * Get the double attribute with name NAME from the event
 * notification NOTIFICATION.
 * Writes the value of the attribute to *VALUE and returns
 * non-zero if the named attribute is found, 0 otherwise.
566 567
 */

Ian Murdock's avatar
Ian Murdock committed
568
int
569 570 571
event_notification_get_double(event_handle_t handle,
                              event_notification_t notification,
                              char *name, double *value)
Ian Murdock's avatar
Ian Murdock committed
572
{
573 574
    elvin_value_t v;

Ian Murdock's avatar
Ian Murdock committed
575 576
    if (!handle || !notification || !name || !value) {
        ERROR("invalid parameter\n");
577 578 579 580
        return 0;
    }

    if (event_notification_get(handle, notification, name, &v) == 0) {
Ian Murdock's avatar
Ian Murdock committed
581
        ERROR("could not get double attribute \"%s\" from notification %p\n",
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
              name, notification);
        return 0;
    }

    *value = v.d;

    return 1;
}


/*
 * Get the int32 attribute with name NAME from the event
 * notification NOTIFICATION.
 * Writes the value of the attribute to *VALUE and returns
 * non-zero if the named attribute is found, 0 otherwise.
 */

int
event_notification_get_int32(event_handle_t handle,
                             event_notification_t notification,
                             char *name, int32_t *value)
{
    elvin_value_t v;

Ian Murdock's avatar
Ian Murdock committed
606 607
    if (!handle || !notification || !name || !value) {
        ERROR("invalid parameter\n");
608 609 610 611
        return 0;
    }

    if (event_notification_get(handle, notification, name, &v) == 0) {
Ian Murdock's avatar
Ian Murdock committed
612
        ERROR("could not get int32 attribute \"%s\" from notification %p\n",
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
              name, notification);
        return 0;
    }

    *value = v.i;

    return 1;
}


/*
 * Get the int64 attribute with name NAME from the event
 * notification NOTIFICATION.
 * Writes the value of the attribute to *VALUE and returns
 * non-zero if the named attribute is found, 0 otherwise.
 */

int
event_notification_get_int64(event_handle_t handle,
                             event_notification_t notification,
                             char *name, int64_t *value)
{
    elvin_value_t v;

Ian Murdock's avatar
Ian Murdock committed
637 638
    if (!handle || !notification || !name || !value) {
        ERROR("invalid parameter\n");
639 640 641 642
        return 0;
    }

    if (event_notification_get(handle, notification, name, &v) == 0) {
Ian Murdock's avatar
Ian Murdock committed
643
        ERROR("could not get int64 attribute \"%s\" from notification %p\n",
644 645 646 647 648 649 650 651 652 653 654 655 656
              name, notification);
        return 0;
    }

    *value = v.h;

    return 1;
}


/*
 * Get the opaque attribute with name NAME from the event
 * notification NOTIFICATION.
Ian Murdock's avatar
Ian Murdock committed
657 658
 * Writes LENGTH bytes into *BUFFER and returns non-zero if the named
 * attribute is found, 0 otherwise.
659 660 661 662 663 664 665 666 667
 */

int
event_notification_get_opaque(event_handle_t handle,
                              event_notification_t notification,
                              char *name, void *buffer, int length)
{
    elvin_value_t v;

Ian Murdock's avatar
Ian Murdock committed
668 669
    if (!handle || !notification || !name || !buffer || !length) {
        ERROR("invalid parameter\n");
670 671 672 673
        return 0;
    }

    if (event_notification_get(handle, notification, name, &v) == 0) {
Ian Murdock's avatar
Ian Murdock committed
674
        ERROR("could not get opaque attribute \"%s\" from notification %p\n",
675 676 677 678 679 680 681 682 683 684 685 686 687
              name, notification);
        return 0;
    }

    memcpy(buffer, v.o.data, length);

    return 1;
}


/*
 * Get the string attribute with name NAME from the event
 * notification NOTIFICATION.
Ian Murdock's avatar
Ian Murdock committed
688 689
 * Writes LENGTH bytes into *BUFFER and returns non-zero if the named
 * attribute is found, 0 otherwise.
690 691 692 693 694 695 696 697 698
 */

int
event_notification_get_string(event_handle_t handle,
                              event_notification_t notification,
                              char *name, char *buffer, int length)
{
    elvin_value_t v;

Ian Murdock's avatar
Ian Murdock committed
699 700
    if (!handle || !notification || !name || !buffer || !length) {
        ERROR("invalid parameter\n");
701 702 703 704
        return 0;
    }

    if (event_notification_get(handle, notification, name, &v) == 0) {
705
	buffer[0] = '\0';
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
        return 0;
    }

    strncpy(buffer, v.s, length);

    return 1;
}


/*
 * Add a double attribute with name NAME and value VALUE to the
 * notification NOTIFICATION.
 * Returns non-zero if the operation is successful, 0 otherwise.
 */

int
event_notification_put_double(event_handle_t handle,
                              event_notification_t notification,
                              char *name, double value)
{
Ian Murdock's avatar
Ian Murdock committed
726 727
    if (!handle || !notification || !name) {
        ERROR("invalid parameter\n");
728 729 730
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
731 732
    TRACE("adding attribute (name=\"%s\", value=%f) to notification %p\n",
          name, value, notification);
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

    if (elvin_notification_add_real64(notification, name, value,
                                      handle->status)
        == 0)
    {
        ERROR("elvin_notification_add_real64 failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}


/*
 * Add an int32 attribute with name NAME and value VALUE to the
 * notification NOTIFICATION.
 * Returns non-zero if the operation is successful, 0 otherwise.
 */

int
event_notification_put_int32(event_handle_t handle,
                             event_notification_t notification,
                             char *name, int32_t value)
{
Ian Murdock's avatar
Ian Murdock committed
758 759
    if (!handle || !notification || !name) {
        ERROR("invalid parameter\n");
760 761 762
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
763 764
    TRACE("adding attribute (name=\"%s\", value=%d) to notification %p\n",
          name, value, notification);
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789

    if (elvin_notification_add_int32(notification, name, value,
                                      handle->status)
        == 0)
    {
        ERROR("elvin_notification_add_int32 failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}


/*
 * Add an int64 attribute with name NAME and value VALUE to the
 * notification NOTIFICATION.
 * Returns non-zero if the operation is successful, 0 otherwise.
 */

int
event_notification_put_int64(event_handle_t handle,
                             event_notification_t notification,
                             char *name, int64_t value)
{
Ian Murdock's avatar
Ian Murdock committed
790 791
    if (!handle || !notification || !name) {
        ERROR("invalid parameter\n");
792 793 794
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
795 796
    TRACE("adding attribute (name=\"%s\", value=%lld) to notification %p\n",
          name, value, notification);
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822

    if (elvin_notification_add_int64(notification, name, value,
                                     handle->status)
        == 0)
    {
        ERROR("elvin_notification_add_int64 failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}


/*
 * Add an opaque attribute with name NAME to the notification
 * NOTIFICATION. The attribute is stored in the buffer BUFFER
 * with length LENGTH.
 * Returns non-zero if the operation is successful, 0 otherwise.
 */

int
event_notification_put_opaque(event_handle_t handle,
                              event_notification_t notification,
                              char *name, void *buffer, int length)
{
Ian Murdock's avatar
Ian Murdock committed
823 824
    if (!handle || !notification || !buffer || !length) {
        ERROR("invalid parameter\n");
825 826 827
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
828 829
    TRACE("adding attribute (name=\"%s\", value=<opaque>) "
          "to notification %p\n", name, notification);
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854

    if (elvin_notification_add_opaque(notification, name, buffer, length,
                                      handle->status)
        == 0)
    {
        ERROR("elvin_notification_add_opaque failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}


/*
 * Add a string attribute with name NAME and value VALUE to the
 * notification NOTIFICATION.
 * Returns non-zero if the operation is successful, 0 otherwise.
 */

int
event_notification_put_string(event_handle_t handle,
                              event_notification_t notification,
                              char *name, char *value)
{
Ian Murdock's avatar
Ian Murdock committed
855 856
    if (!handle || !notification || !name || !value) {
        ERROR("invalid parameter\n");
857 858 859
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
860 861
    TRACE("adding attribute (name=\"%s\", value=\"%s\") to notification %p\n",
          name, value, notification);
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885

    if (elvin_notification_add_string(notification, name, value,
                                      handle->status)
        == 0)
    {
        ERROR("elvin_notification_add_string failed: ");
        elvin_error_fprintf(stderr, handle->status);
        return 0;
    }

    return 1;
}


/*
 * Remove the attribute with name NAME and type TYPE from the event
 * notification NOTIFICATION.  Returns non-zero if the operation is
 * successful, 0 otherwise.
 */

int
event_notification_remove(event_handle_t handle,
                          event_notification_t notification, char *name)
{
Ian Murdock's avatar
Ian Murdock committed
886 887
    if (!handle || !notification || !name) {
        ERROR("invalid parameter\n");
888 889 890
        return 0;
    }

Ian Murdock's avatar
Ian Murdock committed
891 892
    TRACE("removing attribute \"%s\" from notification %p\n",
          name, notification);
893 894

    if (elvin_notification_remove(notification, name, handle->status) == 0) {
Ian Murdock's avatar
Ian Murdock committed
895
        ERROR("elvin_notification_remove failed: ");
896 897
        elvin_error_fprintf(stderr, handle->status);
        return 0;
Ian Murdock's avatar
Ian Murdock committed
898 899 900 901 902
    }

    return 1;
}

903

Ian Murdock's avatar
Ian Murdock committed
904 905 906 907 908 909 910 911 912 913 914 915
struct notify_callback_arg {
    event_notify_callback_t callback;
    void *data;
};

static void notify_callback(elvin_handle_t server,
                            elvin_subscription_t subscription,
                            elvin_notification_t notification, int is_secure,
                            void *rock, elvin_error_t status);

#define EXPRESSION_LENGTH 1024

916 917 918 919 920 921 922 923 924 925
/*
 * Subscribe to events of type TYPE.  Event notifications that match
 * TYPE will be passed to the callback function CALLBACK; DATA is
 * an arbitrary pointer that will be passed to the callback function.
 * Callback functions are of the form
 *
 *     void callback(event_handle_t handle,
 *                   event_notification_t notification,
 *                   void *data);
 *
Ian Murdock's avatar
Ian Murdock committed
926
 * where HANDLE is the handle to the event server, NOTIFICATION is the
927
 * event notification, and DATA is the arbitrary pointer passed to
928 929 930
 * event_subscribe.  Returns a pointer to an event
 * subscription structure if the operation is successful, 0 otherwise.
 */
931

932 933 934 935 936 937 938 939 940 941 942
/*
 * This routine takes a "FOO,BAR" string and breaks it up into
 * separate (TAG==FOO || TAG==BAR) clauses.
 */
static int
addclause(char *tag, char *clause, char *exp, int size, int *index)
{
	int	count = 0;
	char	*bp;
	char    clausecopy[BUFSIZ], *strp = clausecopy;
	char	buf[BUFSIZ];
943
	int     needglob = 1;
944 945

	/* Must copy clause since we use strsep! */
946 947
	if (strlen(clause) >= sizeof(clausecopy)-1)
		goto bad;
948 949 950 951 952 953 954 955 956 957 958
	strcpy(clausecopy, clause);

	/*
	 * Build expression of "or" statements.
	 */
	while (strp && count < sizeof(buf)) {
		bp = strsep(&strp, " ,");

		/* Empty token (two delimiters next to each other) */
		if (! *bp)
			continue;
959 960 961

		if (! strcmp("*", bp))
			needglob = 0;
962 963 964 965 966 967 968
		
		count += snprintf(&buf[count], sizeof(buf) - count,
				  "%s %s == \"%s\" ",
				  (count ? "||" : ""), tag, bp);
	}
	if (strp || count >= sizeof(buf))
		goto bad;
969 970 971 972 973 974 975 976 977 978
#if 0
	if (needglob) {
		count += snprintf(&buf[count], sizeof(buf) - count,
				  "%s %s == \"*\" ",
				  (count ? "||" : ""), tag);

		if (count >= size)
			goto bad;
	}
#endif
979 980 981 982 983 984 985 986 987 988 989 990 991 992
	/*
	 * And wrap in parens (add an "and" if not the first clause).
	 */
	count = snprintf(exp, size, "%s (%s) ", (*index ? "&&" : ""), buf);
	if (count >= size)
		goto bad;
	
	*index += count;
	return 1;
 bad:
	ERROR("Ran out of room for subscription clause: %s %s\n", tag, clause);
	return 0;	
}

Ian Murdock's avatar
Ian Murdock committed
993 994
event_subscription_t
event_subscribe(event_handle_t handle, event_notify_callback_t callback,
995
		address_tuple_t tuple, void *data)
Ian Murdock's avatar
Ian Murdock committed
996 997
{
    elvin_subscription_t subscription;
998
    struct notify_callback_arg *arg;
Ian Murdock's avatar
Ian Murdock committed
999
    char expression[EXPRESSION_LENGTH];
1000
    int index = 0;
Ian Murdock's avatar
Ian Murdock committed
1001 1002 1003 1004 1005

    /* XXX: The declaration of expression has to go last, or the
       local variables on the stack after it get smashed.  Check
       Elvin for buffer overruns. */

1006
    if (!handle || !callback || !tuple) {
Ian Murdock's avatar
Ian Murdock committed
1007
        ERROR("invalid parameter\n");
Ian Murdock's avatar
Ian Murdock committed
1008 1009 1010
        return NULL;
    }

1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
    if (tuple->site &&
	! addclause("SITE", tuple->site,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;

    if (tuple->expt &&
	! addclause("EXPT", tuple->expt,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;

    if (tuple->group &&
	! addclause("GROUP", tuple->group,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;

    if (tuple->host &&
	! addclause("HOST", tuple->host,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;
	
    if (tuple->objtype &&
	! addclause("OBJTYPE", tuple->objtype,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;

    if (tuple->objname &&
	! addclause("OBJNAME", tuple->objname,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;

    if (tuple->eventtype &&
	! addclause("EVENTTYPE", tuple->eventtype,
		    &expression[index], sizeof(expression) - index, &index))
	    return NULL;
	    
1046 1047 1048 1049
    index += snprintf(&expression[index], sizeof(expression) - index,
		     "%s SCHEDULER == %d ",
		     (index ? "&&" : ""),
		     tuple->scheduler);
Ian Murdock's avatar
Ian Murdock committed
1050 1051 1052

    TRACE("subscribing to event %s\n", expression);

1053 1054 1055 1056
    arg = xmalloc(sizeof(*arg));
    /* XXX: Free this in an event_unsubscribe.. */
    arg->callback = callback;
    arg->data = data;
Ian Murdock's avatar
Ian Murdock committed
1057

1058 1059
    subscription = handle->subscribe(handle->server, expression, NULL, 1,
                                     notify_callback, arg, handle->status);
Ian Murdock's avatar
Ian Murdock committed
1060
    if (subscription == NULL) {
1061
        ERROR("could not subscribe to event %s: ", expression);
Ian Murdock's avatar
Ian Murdock committed
1062 1063 1064 1065 1066 1067 1068
        elvin_error_fprintf(stderr, handle->status);
        return NULL;
    }

    return subscription;
}

1069 1070 1071 1072 1073 1074 1075

/*
 * Callback passed to elvin_notification_traverse in
 * event_notification_attr_get.
 * Returns 0 if the desired attribute is found, 1 otherwise.
 */

Ian Murdock's avatar
Ian Murdock committed
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
static int
attr_traverse(void *rock, char *name, elvin_basetypes_t type,
              elvin_value_t value, elvin_error_t error)
{
    struct attr_traverse_arg *arg = (struct attr_traverse_arg *) rock;

    assert(arg);

    /* If this is the name, then set the result value parameter to
       VALUE. */
    if (strcmp(name, arg->name) == 0) {
        *arg->value = value;
        return 0;
    }

    return 1;
}

1094 1095

/*
1096 1097
 * Callback passed to handle->subscribe in event_subscribe. Used to
 * provide our own callback above Elvin's.
1098 1099
 */

Ian Murdock's avatar
Ian Murdock committed
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
static void
notify_callback(elvin_handle_t server,
                elvin_subscription_t subscription,
                elvin_notification_t notification, int is_secure,
                void *rock, elvin_error_t status)
{
    struct event_handle handle;
    struct notify_callback_arg *arg = (struct notify_callback_arg *) rock;
    event_notify_callback_t callback;
    void *data;

    TRACE("received event notification\n");

    assert(arg);
    callback = arg->callback;
    data = arg->data;
    handle.server = server;
    handle.status = status;

1119 1120
    callback(&handle, notification, data);
}
1121

1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
/*
 * address tuple alloc and free.
 */
address_tuple_t
address_tuple_alloc(void)
{
	address_tuple_t	tuple = xmalloc(sizeof(address_tuple));

	bzero(tuple, sizeof(address_tuple));

	return tuple;
}
1134

1135 1136 1137 1138 1139
int
address_tuple_free(address_tuple_t tuple)
{
	free(tuple);
	return 1;
Ian Murdock's avatar
Ian Murdock committed
1140
}