Class: Win32::Daemon

Inherits:
Object
  • Object
show all
Defined in:
ext/win32/daemon.c

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =

The version of this library

0.7.2
CONTINUE_PENDING =

Service has received a signal to resume but is not yet running

INT2NUM(SERVICE_CONTINUE_PENDING)
PAUSE_PENDING =

Service has received a signal to pause but is not yet paused

INT2NUM(SERVICE_PAUSE_PENDING)
PAUSED =

Service is in a paused state

INT2NUM(SERVICE_PAUSED)
RUNNING =

Service is running

INT2NUM(SERVICE_RUNNING)
START_PENDING =

Service has received a signal to start but is not yet running

INT2NUM(SERVICE_START_PENDING)
STOP_PENDING =

Service has received a signal to stop but has not yet stopped

INT2NUM(SERVICE_STOP_PENDING)
STOPPED =

Service is stopped.

INT2NUM(SERVICE_STOPPED)
IDLE =

Service is in an idle state

INT2NUM(0)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.mainloopObject

This is a shortcut for Daemon.new + Daemon#mainloop.



554
555
556
557
558
# File 'ext/win32/daemon.c', line 554

static VALUE daemon_c_mainloop(VALUE klass){
  VALUE v_args[1];
  VALUE v_daemon = rb_class_new_instance(0, v_args, klass);
  return rb_funcall(v_daemon, rb_intern("mainloop"), 0, 0);
}

Instance Method Details

#mainloopObject

This is the method that actually puts your code into a loop and allows it to run as a service. The code that is actually run while in the mainloop is what you defined in your own Daemon#service_main method.



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'ext/win32/daemon.c', line 352

static VALUE daemon_mainloop(VALUE self)
{
  DWORD ThreadId;
  HANDLE events[2];
  DWORD index;
  VALUE result, EventHookHash;
  int status = 0;

  dwServiceState = 0;

  // Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
  // associated with a tty. This helps newbs avoid Errno::EBADF errors.
  if(rb_funcall(rb_stdin, rb_intern("isatty"), 0) == Qtrue)
    rb_funcall(rb_stdin, rb_intern("reopen"), 1, rb_str_new2("NUL"));

  if(rb_funcall(rb_stdout, rb_intern("isatty"), 0) == Qtrue)
    rb_funcall(rb_stdout, rb_intern("reopen"), 1, rb_str_new2("NUL"));

  if(rb_funcall(rb_stderr, rb_intern("isatty"), 0) == Qtrue)
    rb_funcall(rb_stderr, rb_intern("reopen"), 1, rb_str_new2("NUL"));

  // Use a markable instance variable to prevent the garbage collector
  // from freeing the hash before Ruby_Service_Ctrl exits, or just
  // at any ole time while running the service
  EventHookHash = rb_hash_new();
  rb_ivar_set(self, rb_intern("@event_hooks"), EventHookHash);

  // Event hooks
  if(rb_respond_to(self, rb_intern("service_stop"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_STOP),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_stop"))));
  }

  if(rb_respond_to(self, rb_intern("service_pause"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PAUSE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_pause"))));
  }

  if(rb_respond_to(self, rb_intern("service_resume"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_CONTINUE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_resume"))));
  }

  if(rb_respond_to(self, rb_intern("service_interrogate"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_INTERROGATE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_interrogate"))));
  }

  if(rb_respond_to(self, rb_intern("service_shutdown"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_SHUTDOWN),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_shutdown"))));
  }

#ifdef SERVICE_CONTROL_PARAMCHANGE
  if(rb_respond_to(self, rb_intern("service_paramchange"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_PARAMCHANGE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_paramchange"))));
  }
#endif

#ifdef SERVICE_CONTROL_NETBINDADD
  if(rb_respond_to(self, rb_intern("service_netbindadd"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDADD),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindadd"))));
  }
#endif

#ifdef SERVICE_CONTROL_NETBINDREMOVE
  if(rb_respond_to(self, rb_intern("service_netbindremove"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDREMOVE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindremove"))));
  }
#endif

#ifdef SERVICE_CONTROL_NETBINDENABLE
  if(rb_respond_to(self, rb_intern("service_netbindenable"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDENABLE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbindenable"))));
  }
#endif

#ifdef SERVICE_CONTROL_NETBINDDISABLE
  if(rb_respond_to(self, rb_intern("service_netbinddisable"))){
    rb_hash_aset(EventHookHash, INT2NUM(SERVICE_CONTROL_NETBINDDISABLE),
      rb_ary_new3(2, self, INT2NUM(rb_intern("service_netbinddisable"))));
  }
#endif

  // Calling init here so that init failures never even tries to
  // start the service... of course that means that init methods
  // must be very quick, because the SCM will be receiving no
  // START_PENDING messages while init's running - I may fix this
  // later
  if(rb_respond_to(self, rb_intern("service_init")))
    rb_funcall(self, rb_intern("service_init"),0);

  // Create the event to signal the service to start.
  hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  if(hStartEvent == NULL)
    rb_raise(cDaemonError, ErrorDescription(GetLastError()));

  // Create the event to signal the service to stop.
  hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  if(hStopEvent == NULL)
    rb_raise(cDaemonError, ErrorDescription(GetLastError()));

  // Create the event to signal the service that stop has completed
  hStopCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  if(hStopCompletedEvent == NULL)
    rb_raise(cDaemonError, ErrorDescription(GetLastError()));

  // Create Thread for service main
  hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, &ThreadId);

  if(hThread == INVALID_HANDLE_VALUE)
    rb_raise(cDaemonError, ErrorDescription(GetLastError()));

  events[0] = hThread;
  events[1] = hStartEvent;

  // wait for Service_Main function to either start the service OR terminate
  while((index = WaitForMultipleObjects(2,events,FALSE,1000)) == WAIT_TIMEOUT)
  {
  }

  // thread exited, so the show is off
  if(index == WAIT_OBJECT_0)
    rb_raise(cDaemonError, "Service_Main thread exited abnormally");

  // from this point onward, stopevent must be triggered!

  // Create the green thread to poll for Service_Ctrl events
  rb_thread_create(Ruby_Service_Ctrl, (void *)self);

  result = rb_protect(daemon_mainloop_protect, self, &status);

  // service_main raised an exception
  if(status){
    daemon_mainloop_ensure(self);
    rb_jump_tag(status);
  }

  // service_main exited cleanly
  return daemon_mainloop_ensure(self);
}

#running?Boolean

Returns whether or not the service is in a running state, i.e. the service status is either RUNNING, PAUSED or IDLE.

This is typically used within your service_main method to setup the main loop. For example:

class MyDaemon < Daemon
   def service_main
      while running?
         # Your main loop here
      end
   end
end

Returns:

  • (Boolean)


537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'ext/win32/daemon.c', line 537

static VALUE daemon_is_running(VALUE self){
  VALUE v_bool = Qfalse;

  if(
    (dwServiceState == SERVICE_RUNNING) ||
    (dwServiceState == SERVICE_PAUSED) ||
    (dwServiceState == 0)
  ){
    v_bool = Qtrue;
  }

  return v_bool;
}

#stateObject

Returns the state of the service (as an constant integer) which can be any of the service status constants, e.g. RUNNING, PAUSED, etc.

This method is typically used within your service_main method to setup the loop. For example:

class MyDaemon < Daemon
   def service_main
      while state == RUNNING || state == PAUSED || state == IDLE
         # Your main loop here
      end
   end
end

See the Daemon#running? method for an abstraction of the above code.



518
519
520
# File 'ext/win32/daemon.c', line 518

static VALUE daemon_state(VALUE self){
  return UINT2NUM(dwServiceState);
}