Example #1
0
static ACE_THR_FUNC_RETURN
asynchronous_signal_handler (void *)
{
  ACE_Sig_Set sigset;

  // Register signal handlers.
  if (child)
    {
      sigset.sig_add (SIGINT);
      sigset.sig_add (SIGTERM);
    }
  else
    {
      sigset.sig_add (SIGCHLD);
      sigset.sig_add (SIGHUP);
    }

  // Register the <handle_signal> method to process all the signals in
  // <sigset>.
  ACE_Sig_Action sa (sigset,
                     (ACE_SignalHandler) handle_signal);
  ACE_UNUSED_ARG (sa);

  return 0;
}
Example #2
0
bool
Lorica::Proxy::setup_shutdown_handler(void)
{
	ACE_Sig_Set sigset;

	// Register signal handlers.
	sigset.sig_add(SIGINT);
	sigset.sig_add(SIGUSR1);
	sigset.sig_add(SIGUSR2);
	sigset.sig_add(SIGTERM);

	// Register the <handle_signal> method to process all the signals in
	// <sigset>.
	ACE_Sig_Action sa(sigset,
			  (ACE_SignalHandler)Lorica::signal_handler);
	ACE_UNUSED_ARG(sa);

	return true;
}
Example #3
0
static ACE_THR_FUNC_RETURN
synchronous_signal_handler (void *)
{
  ACE_Sig_Set sigset;

  // Register signal handlers.
  if (child)
    {
      sigset.sig_add (SIGINT);
      sigset.sig_add (SIGTERM);
    }
  else
    sigset.sig_add (SIGHUP);

  for (;;)
    {
      // Block waiting for SIGINT, SIGTERM, or SIGHUP, depending on
      // whether we're the parent or child process.
      int signr = ACE_OS::sigwait (sigset);
      if (signr == -1)
        {
          if (errno != EINTR)
            ACE_ERROR ((LM_ERROR,
                        ACE_TEXT ("(%P|%t) %p\n"),
                        ACE_TEXT ("sigwait")));
          continue;
        }
      if (handle_signal (signr) == -1)
        break;
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("(%P|%t) handled signal\n")));
    }

  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("(%P|%t) synchronous signal handler done\n")));

  return 0;
}
Example #4
0
// Listing 3
// Listing 2 code/ch11
static void register_actions ()
{
  ACE_TRACE ("::register_actions");

  ACE_Sig_Action sa (reinterpret_cast <ACE_SignalHandler> (my_sighandler));

  // Make sure we specify that SIGUSR1 will be masked out
  // during the signal handler's execution.
  ACE_Sig_Set ss;
  ss.sig_add (SIGUSR1);
  sa.mask (ss);

  // Register the same handler function for these
  // two signals.
  sa.register_action (SIGUSR1);
  sa.register_action (SIGUSR2);
}
int main(int argc, char *argv[]){
	if (argc > 1){
		cout << "Please do not use command!" << endl;
		return TOO_MANY_COMMANDS_ERROR;
	}
	cout << argv[0] << endl;

	Server server;
	ACE_Reactor::instance()->register_handler(&server, ACE_Event_Handler::ACCEPT_MASK);

	ACE_Sig_Set sigset;
	sigset.sig_add(SIGINT);
	ACE_Reactor::instance()->register_handler(sigset, &server);
	ACE_Reactor::instance()->register_handler(&server, ACE_Event_Handler::SIGNAL_MASK);
	ACE_Reactor::instance()->run_reactor_event_loop();

	return SUCCESS;
}
static void
register_signal_handlers (void)
{
  // Register SIGQUIT (never blocked).
  ACE_Sig_Action sigquit ((ACE_SignalHandler) signal_handler,
			  SIGQUIT);
  ACE_UNUSED_ARG (sigquit);

  // Don't let the SIGALRM interrupt the SIGINT handler!
  ACE_Sig_Set ss;
  ss.sig_add (SIGALRM);

  // Register SIGINT (note that system calls will be restarted
  // automatically).
  ACE_Sig_Action sigint ((ACE_SignalHandler) signal_handler,
			 SIGINT,
			 ss,
			 SA_RESTART);
  ACE_UNUSED_ARG (sigint);
}
int ACE_TMAIN (int, ACE_TCHAR *[])
{

  MySignalHandler sighandler;
  ACE_Sig_Handler sh;
  sh.register_handler (SIGUSR1, &sighandler);

  ACE_Sig_Set ss;
  ss.sig_add (SIGUSR1);

  ACE_Sig_Guard guard (&ss);
  {
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("Entering critical region\n")));
    ACE_OS::sleep (10);
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("Leaving  critical region\n")));
  }

  // Do other stuff.

  return 0;
}
Example #8
0
int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try
    {
      // The usual server side boilerplate code.

      CORBA::ORB_var orb = CORBA::ORB_init (argc, argv);

      CORBA::Object_var obj =
        orb->resolve_initial_references ("RootPOA");

      PortableServer::POA_var root_poa =
        PortableServer::POA::_narrow (obj.in ());

      PortableServer::POAManager_var poa_manager =
        root_poa->the_POAManager ();

      poa_manager->activate ();

      // "built-in" strategies are the following:
      //   0 = RoundRobin
      //   1 = Random
      //   2 = LeastLoaded
      int default_strategy = 1;

      // Check the non-ORB arguments.
      ::parse_args (argc,
                    argv,
                    default_strategy);

      TAO_LB_LoadManager * lm = 0;
      ACE_NEW_THROW_EX (lm,
        TAO_LB_LoadManager(::ping_timeout_milliseconds,
                           ::ping_interval_seconds),
                          CORBA::NO_MEMORY (
                          CORBA::SystemException::_tao_minor_code (
                            TAO::VMCID,
                            ENOMEM),
                          CORBA::COMPLETED_NO));

      PortableServer::ServantBase_var safe_lm = lm;

      // Initalize the LoadManager servant.
      lm->initialize (orb->orb_core ()->reactor (),
                      orb.in (),
                      root_poa.in ());

      PortableGroup::Properties props (1);
      props.length (1);
      props[0].nam.length (1);
      props[0].nam[0].id =
        CORBA::string_dup ("org.omg.CosLoadBalancing.StrategyInfo");

      CosLoadBalancing::StrategyInfo strategy_info;

      switch (default_strategy)
        {
        case 0:
          strategy_info.name = CORBA::string_dup ("RoundRobin");
          break;
        case 1:
          strategy_info.name = CORBA::string_dup ("Random");
          break;
        case 2:
          strategy_info.name = CORBA::string_dup ("LeastLoaded");
          break;
        default:
          ORBSVCS_ERROR_RETURN ((LM_ERROR,
                            ACE_TEXT ("ERROR: LoadBalancer internal error.\n")
                            ACE_TEXT ("       Unknown built-in strategy.\n")),
                            -1);
        }

      props[0].val <<= strategy_info;

      lm->set_default_properties (props);

      CosLoadBalancing::LoadManager_var load_manager =
        lm->_this ();

      CORBA::String_var str =
        orb->object_to_string (load_manager.in ());

      // to support corbaloc
      // Get a reference to the IOR table.
      CORBA::Object_var tobj = orb->resolve_initial_references ("IORTable");

      IORTable::Table_var table = IORTable::Table::_narrow (tobj.in ());

      // bind your stringified IOR in the IOR table
      table->bind ("LoadManager", str.in ());

      FILE * lm_ior = ACE_OS::fopen (lm_ior_file, "w");
      ACE_OS::fprintf (lm_ior, "%s", str.in ());
      ACE_OS::fclose (lm_ior);

#if defined (linux) && defined (ACE_HAS_THREADS)
      if (ACE_Thread_Manager::instance ()->spawn (::TAO_LB_run_load_manager,
                                                  orb.in ()) == -1)
        {
          ORBSVCS_ERROR_RETURN ((LM_ERROR,
                             "ERROR:  Unable to spawn TAO LoadManager's "
                             "ORB thread.\n"),
                            -1);
        }

      ACE_Sig_Set sigset;
      sigset.sig_add (SIGINT);
      sigset.sig_add (SIGTERM);

      int signum = -1;

      // Block waiting for the registered signals.
      if (ACE_OS::sigwait (sigset, &signum) == -1)
        {
          ORBSVCS_ERROR_RETURN ((LM_ERROR,
                             "(%P|%t) %p\n",
                             "ERROR waiting on signal"),
                            -1);
        }

      ACE_ASSERT (signum == SIGINT || signum == SIGTERM);
#else
      // Activate/register the signal handler that (attempts) to
      // ensure graceful shutdown of the LoadManager so that remote
      // resources created by the LoadManager can be cleaned up.
      TAO_LB_Signal_Handler signal_handler (orb.in (), root_poa.in ());

      if (signal_handler.activate () != 0)
        {
          ORBSVCS_ERROR_RETURN ((LM_ERROR,
                             "Error: can't activate LB signal handler, exiting.\n"),
                             -1);
        }

      // @@ There is a subtle race condition here.  If the signal
      //    handler thread shuts down the ORB before it is run, the
      //    below call to ORB::run() will throw a CORBA::BAD_INV_ORDER
      //    exception.
      orb->run ();

      // Wait for the signal handler thread to finish
      // before the process exits.
      signal_handler.wait ();
#endif  /* linux && ACE_HAS_THREADS */

      orb->destroy ();
    }
//   catch (const PortableGroup::InvalidProperty& ex)
//     {
//       ORBSVCS_DEBUG ((LM_DEBUG, "Property ----> %s\n", ex.nam[0].id.in ()));
//     }
  catch (const CORBA::Exception& ex)
    {
      ex._tao_print_exception ("TAO Load Manager");

      return -1;
    }

  return 0;
}
Example #9
0
int
ACE_Service_Config::open_i (const ACE_TCHAR program_name[],
                            const ACE_TCHAR *logger_key,
                            bool ,
                            bool ,
                            bool )
{
  ACE_TRACE ("ACE_Service_Config::open_i");
  ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->lock_, -1));

  ACE_Log_Msg *log_msg = ACE_LOG_MSG;

  if(ACE::debug ())
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("ACE (%P|%t) SC::open_i - this=%@, opened=%d\n"),
                this, this->is_opened_));

  // Guard against reentrant processing.
  if(this->is_opened_)
    return 0;

  this->is_opened_ = true;

  // Check for things we need to do on a per-process basis and which
  // may not be safe, or wise to do an a per instance basis

  // Become a daemon before doing anything else.
  if(ACE_Service_Config::be_a_daemon_)
    ACE::daemonize ();

  // Write process id to file.
  if(this->pid_file_name_ != 0)
    {
      FILE* pidf = ACE_OS::fopen (this->pid_file_name_,
                                  ACE_TEXT("w"));

      if(pidf != 0)
        {
          ACE_OS::fprintf (pidf,
                           "%ld\n",
                           static_cast<long> (ACE_OS::getpid()));
          ACE_OS::fclose (pidf);
        }
    }

  u_long flags = log_msg->flags ();

  // Only use STDERR if the caller hasn't already set the flags.
  if(flags == 0)
    flags = (u_long) ACE_Log_Msg::STDERR;

  const ACE_TCHAR *key = logger_key;

  if(key == 0 || ACE_OS::strcmp (key, ACE_DEFAULT_LOGGER_KEY) == 0)
    {
      // Only use the static <logger_key_> if the caller doesn't
      // override it in the parameter list or if the key supplied is
      // equal to the default static logger key.
      key = ACE_Service_Config::current()->logger_key_;
    }
  else
    {
      ACE_SET_BITS (flags, ACE_Log_Msg::LOGGER);
    }

  if(log_msg->open (program_name,
                     flags,
                     key) == -1)
    return -1;

  if(ACE::debug ())
    ACE_DEBUG ((LM_STARTUP,
                ACE_TEXT ("starting up daemon %n\n")));

  // Initialize the Service Repository (this will still work if
  // user forgets to define an object of type ACE_Service_Config).
  ACE_Service_Repository::instance (ACE_Service_Gestalt::MAX_SERVICES);

  // Initialize the ACE_Reactor (the ACE_Reactor should be the
  // same size as the ACE_Service_Repository).
  ACE_Reactor::instance ();

  // There's no point in dealing with this on NT since it doesn't
  // really support signals very well...
#if !defined (ACE_LACKS_UNIX_SIGNALS)
  // Only attempt to register a signal handler for positive
  // signal numbers.
  if(ACE_Service_Config::signum_ > 0)
    {
      ACE_Sig_Set ss;
      ss.sig_add (ACE_Service_Config::signum_);
      if((ACE_Reactor::instance () != 0) &&
          (ACE_Reactor::instance ()->register_handler
           (ss, ACE_Service_Config::signal_handler_) == -1))
        ACE_ERROR ((LM_ERROR,
                    ACE_TEXT ("can't register signal handler\n")));
    }
#endif /* ACE_LACKS_UNIX_SIGNALS */

  return 0;
}
int
ACE_Service_Config::open_i (const ACE_TCHAR program_name[],
                            const ACE_TCHAR *logger_key,
                            bool ignore_static_svcs,
                            bool ignore_default_svc_conf_file,
                            bool ignore_debug_flag)
{
  int result = 0;
  ACE_TRACE ("ACE_Service_Config::open_i");
  ACE_Log_Msg *log_msg = ACE_LOG_MSG;

  if (ACE::debug ())
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("(%P|%t) SC::open_i - this=%@, opened=%d, ")
                ACE_TEXT ("loadstatics=%d\n"),
                this, this->is_opened_, this->no_static_svcs_));

  // Guard against reentrant processing. For example,
  // if the singleton gestalt (ubergestalt) was already open,
  // do not open it again...
  // The base class open_i increments this and we are
  // forwarding to it, so we don't have to increment here.
  if (this->is_opened_ != 0)
    return ACE_Service_Gestalt::open_i (program_name,
                                        logger_key,
                                        ignore_static_svcs,
                                        ignore_default_svc_conf_file,
                                        ignore_debug_flag);

  // Check for things we need to do on a per-process basis and which
  // may not be safe, or wise to do an a per instance basis

  // Override any defaults, if required
  this->no_static_svcs_ = ignore_static_svcs;

  // Become a daemon before doing anything else.
  if (this->be_a_daemon_)
    ACE::daemonize ();

  // Write process id to file.
  if (this->pid_file_name_ != 0)
    {
      FILE* pidf = ACE_OS::fopen (this->pid_file_name_,
                                  ACE_TEXT("w"));

      if (pidf != 0)
        {
          ACE_OS::fprintf (pidf,
                           "%ld\n",
                           static_cast<long> (ACE_OS::getpid()));
          ACE_OS::fclose (pidf);
        }
    }

  u_long flags = log_msg->flags ();

  // Only use STDERR if the caller hasn't already set the flags.
  if (flags == 0)
    flags = (u_long) ACE_Log_Msg::STDERR;

  const ACE_TCHAR *key = logger_key;

  if (key == 0 || ACE_OS::strcmp (key, ACE_DEFAULT_LOGGER_KEY) == 0)
    // Only use the static <logger_key_> if the caller doesn't
    // override it in the parameter list or if the key supplied is
    // equal to the default static logger key.
    key = this->logger_key_;
  else
    ACE_SET_BITS (flags, ACE_Log_Msg::LOGGER);

  if (log_msg->open (program_name,
                     flags,
                     key) == -1)
    result = -1;
  else
    {
      if (ACE::debug ())
        ACE_DEBUG ((LM_STARTUP,
                    ACE_TEXT ("starting up daemon %n\n")));

      // Initialize the Service Repository (this will still work if
      // user forgets to define an object of type ACE_Service_Config).
      ACE_Service_Repository::instance (ACE_Service_Config::MAX_SERVICES);

      // Initialize the ACE_Reactor (the ACE_Reactor should be the
      // same size as the ACE_Service_Repository).
      ACE_Reactor::instance ();

      // There's no point in dealing with this on NT since it doesn't
      // really support signals very well...
#if !defined (ACE_LACKS_UNIX_SIGNALS)
      // Only attempt to register a signal handler for positive
      // signal numbers.
      if (ACE_Service_Config::signum_ > 0)
        {
          ACE_Sig_Set ss;
          ss.sig_add (ACE_Service_Config::signum_);
          if (ACE_Reactor::instance ()->register_handler
              (ss, ACE_Service_Config::signal_handler_) == -1)
            ACE_ERROR ((LM_ERROR,
                        ACE_TEXT ("can't register signal handler\n")));
        }
#endif /* ACE_LACKS_UNIX_SIGNALS */
    }

  if (result == -1)
    return -1;

  if (this->init_svc_conf_file_queue () == -1)
    return -1;

  // Check if the default file exists before attempting to queue it
  // for processing
  if (!ignore_default_svc_conf_file)
    {
      FILE *fp = ACE_OS::fopen (ACE_DEFAULT_SVC_CONF,
                                ACE_TEXT ("r"));
      ignore_default_svc_conf_file = (fp == 0);
      if (fp != 0)
        ACE_OS::fclose (fp);
    }

  if (!ignore_default_svc_conf_file
      && this->svc_conf_file_queue_->is_empty ())
    {
      // Load the default "svc.conf" entry here if there weren't
      // overriding -f arguments in <parse_args>.
      if (this->svc_conf_file_queue_->enqueue_tail
          (ACE_TString (ACE_DEFAULT_SVC_CONF)) == -1)
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("%p\n"),
                             ACE_TEXT ("enqueuing ")
                             ACE_DEFAULT_SVC_CONF
                             ACE_TEXT(" file")),
                            -1);
        }
    }

  return ACE_Service_Gestalt::open_i (program_name,
                                      logger_key,
                                      ignore_static_svcs,
                                      ignore_default_svc_conf_file,
                                      ignore_debug_flag);
}
int main(int argc, char *argv[])
{
  int rc = 0;
  bool first = true;

  ImageVector v;

  Client client(argc, argv);

  if ((rc = parseArgs(argc, argv)))
    return rc;

  try {
    // event handler
    Event * event = new Event();
    
    // Signal set to be handled by the event handler.
    ACE_Sig_Set sig;
    
    // register Signal handler for Ctr+C
    sig.sig_add(SIGINT);
    sig.sig_add(SIGTERM);
    
    // Reactor, misused as signal handler
    ACE_Reactor reactor;
    
    if (reactor.register_handler(sig, event) == -1) {
      throw Miro::ACE_Exception(errno, "failed to register signal handler");
    }
    
    // get reference to video service
    Video_var video = client.resolveName<Video>(interfaceName.c_str());
    Miro::VideoConnection connection(video.in());

    ACE_Time_Value start;
    while(!canceled) {

      // wait for key
      if (first || !streaming) {
	first = false;
	cout << "Press key to grab next image: " << flush;
	getchar();
	start = ACE_OS::gettimeofday();
      }
    
      // init streaming timer
      if (!first && streaming) {
	ACE_Time_Value elapsed = ACE_OS::gettimeofday();
	elapsed -= start;
	if (elapsed.msec() > stop)
	  break;
      }

      {
	// get image
	Miro::VideoAcquireImage 
	  frame(connection, 
		(interval)?
		Miro::VideoAcquireImage::Current :
		Miro::VideoAcquireImage::Next);

	// fill image structure
	Image image;
	image.fileName = path() + client.namingContextName + "_" + Miro::timeString() + ".ppm";
	image.width = connection.handle->format.width;
	image.height = connection.handle->format.height;
	
	// fill image buffer
	if (!bgr && !streaming)
	  image.buffer = (char*) frame.buffer;
	else {
	  image.buffer = new char[3 * image.width * image.height];

	  // byte swapping
	  if (bgr) {
	    int offset = 0;
	    for (int i = 0; i < image.width; ++i) {
	      for (int j = 0; j < image.height; ++j, offset += 3) {
		image.buffer[offset + 0] = frame.buffer[offset + 2]; // r
		image.buffer[offset + 1] = frame.buffer[offset + 1]; // g
		image.buffer[offset + 2] = frame.buffer[offset + 0]; // b
	      }
	    }
	  }
	  else
	    memcpy(image.buffer, frame.buffer, 3 * image.width * image.height);
	}
	
	// save image
	if (!streaming) {
	  image.writePpm();
	  if (bgr)
	    delete[] image.buffer;
	}
	else 
	  v.push_back(image);
      }

      // sleep
      if (interval) {
	ACE_Time_Value delta;
	delta.msec(interval);
	ACE_OS::sleep(delta);
      }
    }
    cout << "exiting on signal" << endl;

    for (ImageVector::const_iterator i = v.begin(); i != v.end(); ++i) {
      i->writePpm();
      delete i->buffer;
    }
  }
  catch (const Miro::ETimeOut& e) {
    cerr << "Miro Timeout Exception: " << e << endl;
    rc = 1;
  }
  catch (const Miro::EDevIO & e) {
    cerr << "Miro Device I/O exception: " << e << endl;
    rc = 1;
  }
  catch (const Miro::EOutOfBounds & e) {
    cerr << "Miro out of bounds exception: " << e << endl;
    rc = 1;
  }
  catch (const CORBA::Exception & e) {
    cerr << "Uncaught CORBA exception: " << e << endl;
    rc = 1;
  }
  return rc;
}
Example #12
0
void
do_initializeSignals (bool allowUserRuntimeConnect_in,
                      ACE_Sig_Set& signals_inout)
{
  RPG_TRACE(ACE_TEXT("::do_initializeSignals"));

  int result = -1;

  // init return value(s)
  result = signals_inout.empty_set ();
  if (result == -1)
  {
    ACE_DEBUG ((LM_ERROR,
                ACE_TEXT ("failed to ACE_Sig_Set::empty_set(): \"%m\", aborting\n")));
    return;
  } // end IF

  // init list of handled signals...
  // *PORTABILITY*: on Windows SIGHUP and SIGQUIT are not defined,
  // so we handle SIGBREAK (21) and SIGABRT (22) instead...
#if !defined (ACE_WIN32) && !defined (ACE_WIN64)
  // *NOTE*: don't handle SIGHUP !!!! --> program will hang !
  //signals_inout.push_back(SIGHUP);
#endif
  signals_inout.sig_add (SIGINT);
#if !defined (ACE_WIN32) && !defined (ACE_WIN64)
  signals_inout.sig_add (SIGQUIT);
#endif
//   signals_inout.sig_add(SIGILL);
//   signals_inout.sig_add(SIGTRAP);
//   signals_inout.sig_add(SIGBUS);
//   signals_inout.sig_add(SIGFPE);
//   signals_inout.sig_add(SIGKILL); // cannot catch this one...
  if (allowUserRuntimeConnect_in)
#if !defined (ACE_WIN32) && !defined (ACE_WIN64)
    signals_inout.sig_add (SIGUSR1);
#else
    signals_inout.sig_add (SIGBREAK);
#endif
//   signals_inout.sig_add(SIGSEGV);
  if (allowUserRuntimeConnect_in)
#if !defined (ACE_WIN32) && !defined (ACE_WIN64)
    signals_inout.sig_add (SIGUSR2);
#else
    signals_inout.sig_add (SIGABRT);
#endif
//   signals_inout.sig_add(SIGPIPE);
//   signals_inout.sig_add(SIGALRM);
  signals_inout.sig_add (SIGTERM);
//   signals_inout.sig_add(SIGSTKFLT);
//   signals_inout.sig_add(SIGCHLD);
//   signals_inout.sig_add(SIGCONT);
//   signals_inout.sig_add(SIGSTOP); // cannot catch this one...
//   signals_inout.sig_add(SIGTSTP);
//   signals_inout.sig_add(SIGTTIN);
//   signals_inout.sig_add(SIGTTOU);
//   signals_inout.sig_add(SIGURG);
//   signals_inout.sig_add(SIGXCPU);
//   signals_inout.sig_add(SIGXFSZ);
//   signals_inout.sig_add(SIGVTALRM);
//   signals_inout.sig_add(SIGPROF);
//   signals_inout.sig_add(SIGWINCH);
//   signals_inout.sig_add(SIGIO);
//   signals_inout.sig_add(SIGPWR);
//   signals_inout.sig_add(SIGSYS);
//   signals_inout.sig_add(SIGRTMIN);
//   signals_inout.sig_add(SIGRTMIN+1);
// ...
//   signals_inout.sig_add(SIGRTMAX-1);
//   signals_inout.sig_add(SIGRTMAX);
}