Ejemplo n.º 1
0
int
fiber_channel_check_wait(struct fiber_channel *ch, ev_tstamp start_time,
		       ev_tstamp timeout)
{
	/*
	 * Preconditions of waiting are:
	 * - the channel is not closed,
	 * - the current fiber has not been
	 *   cancelled,
	 * - the timeout has not expired.
	 * If timeout is non-zero, yield at least once, otherwise
	 * rounding errors can lead to an infinite loop in the
	 * caller, since ev_now() does not get updated without
	 * a yield.
	 */
	if (ch->is_closed) {
		diag_set(ChannelIsClosed);
		return -1;
	}
	if (fiber_is_cancelled()) {
		diag_set(FiberIsCancelled);
		return -1;
	}
	if (timeout == 0 || ev_monotonic_now(loop()) > start_time + timeout) {
		diag_set(TimedOut);
		return -1;
	}
	return 0;
}
Ejemplo n.º 2
0
static int
memtx_bitset_index_register_tuple(struct memtx_bitset_index *index,
				  struct tuple *tuple)
{
	uint32_t id;
	struct tuple **place;
	if (index->spare_id != SPARE_ID_END) {
		id = index->spare_id;
		void *mem = matras_get(index->id_to_tuple, id);
		index->spare_id = *(uint32_t *)mem;
		place = (struct tuple **)mem;
	} else {
		place = (struct tuple **)matras_alloc(index->id_to_tuple, &id);
	}
	*place = tuple;

	struct bitset_hash_entry entry;
	entry.id = id;
	entry.tuple = tuple;
	uint32_t pos = mh_bitset_index_put(index->tuple_to_id, &entry, 0, 0);
	if (pos == mh_end(index->tuple_to_id)) {
		*(uint32_t *)tuple = index->spare_id;
		index->spare_id = id;
		diag_set(OutOfMemory, (ssize_t) pos, "hash", "key");
		return -1;
	}
	return 0;
}
Ejemplo n.º 3
0
/**
 * Populate key options from their msgpack-encoded representation
 * (msgpack map).
 */
int
opts_decode(void *opts, const struct opt_def *reg, const char **map,
	    uint32_t errcode, uint32_t field_no, struct region *region)
{
	assert(mp_typeof(**map) == MP_MAP);

	/*
	 * The implementation below has O(map_size * reg_size) complexity.
	 * DDL is not performance-critical, so this is not a problem.
	 */
	uint32_t map_size = mp_decode_map(map);
	for (uint32_t i = 0; i < map_size; i++) {
		if (mp_typeof(**map) != MP_STR) {
			diag_set(ClientError, errcode, field_no,
				 "key must be a string");
			return -1;
		}
		uint32_t key_len;
		const char *key = mp_decode_str(map, &key_len);
		if (opts_parse_key(opts, reg, key, key_len, map, errcode,
				   field_no, region, false) != 0)
			return -1;
	}
	return 0;
}
Ejemplo n.º 4
0
int main(int argc, const char *argv[]) {
   const char **arg1;
#if 0
   unsigned int n = 1024;	/* this is default on my Linux */
#endif

   diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);

   arg1 = argv+1;  --argc;
   while (arg1[0] && (arg1[0][0] == '-')) {
      switch (arg1[0][1]) {
#if WITH_HELP
      case '?': case 'h': procan_usage(stdout); exit(0);
#endif /* WITH_HELP */
      case 'c': procan_cdefs(stdout); exit(0);
#if LATER
      case 'V': procan_version(stdout); exit(0);
      case 'l': diag_set(arg1[0][2], &arg1[0][3]); break;
      case 'd': diag_set('d', NULL); break;
#endif
#if 0
      case 'n': n = strtoul(&arg1[0][2], NULL, 0); break;
#endif
      case '\0': break;
      default:
	 diag_set_int('e', E_FATAL);
	 Error1("unknown option \"%s\"", arg1[0]);
#if WITH_HELP
	 procan_usage(stderr);
#endif
	 exit(1);
      }
      if (arg1[0][1] == '\0')
	 break;
      ++arg1; --argc;
   }
   if (argc != 0) {
      Error1("%d superfluous arguments", argc);
#if WITH_HELP
      procan_usage(stderr);
#endif
      exit(1);
   }
   procan(stdout);
   hostan(stdout);
   return 0;
}
Ejemplo n.º 5
0
static int
blackhole_space_execute_upsert(struct space *space, struct txn *txn,
			       struct request *request)
{
	(void)space;
	(void)txn;
	(void)request;
	diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "upsert()");
	return -1;
}
Ejemplo n.º 6
0
static int
blackhole_space_execute_delete(struct space *space, struct txn *txn,
			       struct request *request, struct tuple **result)
{
	(void)space;
	(void)txn;
	(void)request;
	(void)result;
	diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "delete()");
	return -1;
}
Ejemplo n.º 7
0
/**
 * Open file descriptor and lock it.
 */
int
path_lock(const char *path, int *lock)
{
        int fd = open(path, O_RDONLY);
	if (fd < 0) {
		diag_set(SystemError, "Can't open path: %s", path);
		return -1;
	}
	if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
		if (errno != EWOULDBLOCK) {
			diag_set(SystemError, "Can't lock path: %s", path);
			close(fd);
			return -1;
		}
		close(fd);
		fd = -1;
	}
	*lock= fd;
	return 0;
}
Ejemplo n.º 8
0
void
luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg,
		   struct mpstream *stream, int index)
{
	struct tuple *tuple = luaT_istuple(L, index);
	if (tuple != NULL) {
		return tuple_to_mpstream(tuple, stream);
	} else if (luamp_encode(L, cfg, stream, index) != MP_ARRAY) {
		diag_set(ClientError, ER_TUPLE_NOT_ARRAY);
		luaT_error(L);
	}
}
Ejemplo n.º 9
0
struct fiber_channel *
fiber_channel_new(uint32_t size)
{
	struct fiber_channel *res = (struct fiber_channel *)
		malloc(fiber_channel_memsize(size));
	if (res == NULL) {
		diag_set(OutOfMemory, size,
			 "malloc", "struct fiber_channel");
		return NULL;
	}
	fiber_channel_create(res, size);
	return res;
}
Ejemplo n.º 10
0
static inline int
lbox_catch(lua_State *L)
{
	struct error *e = luaL_iserror(L, -1);
	if (e != NULL) {
		/* Re-throw original error */
		diag_add_error(&fiber()->diag, e);
	} else {
		/* Convert Lua error to a Tarantool exception. */
		diag_set(LuajitError, lua_tostring(L, -1));
	}
	return 1;
}
Ejemplo n.º 11
0
struct engine *
blackhole_engine_new(void)
{
	struct engine *engine = calloc(1, sizeof(*engine));
	if (engine == NULL) {
		diag_set(OutOfMemory, sizeof(*engine),
			 "malloc", "struct engine");
		return NULL;
	}

	engine->vtab = &blackhole_engine_vtab;
	engine->name = "blackhole";
	engine->flags = ENGINE_BYPASS_TX;
	return engine;
}
Ejemplo n.º 12
0
static struct space *
blackhole_engine_create_space(struct engine *engine, struct space_def *def,
			      struct rlist *key_list)
{
	if (!rlist_empty(key_list)) {
		diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "indexes");
		return NULL;
	}

	struct space *space = (struct space *)calloc(1, sizeof(*space));
	if (space == NULL) {
		diag_set(OutOfMemory, sizeof(*space),
			 "malloc", "struct space");
		return NULL;
	}

	/* Allocate tuples on runtime arena, but check space format. */
	struct tuple_format *format;
	format = tuple_format_new(&tuple_format_runtime->vtab, NULL, NULL, 0,
				  def->fields, def->field_count,
				  def->exact_field_count, def->dict, false,
				  false);
	if (format == NULL) {
		free(space);
		return NULL;
	}
	tuple_format_ref(format);

	if (space_create(space, engine, &blackhole_space_vtab,
			 def, key_list, format) != 0) {
		tuple_format_unref(format);
		free(space);
		return NULL;
	}
	return space;
}
Ejemplo n.º 13
0
static int
execute_lua_eval(lua_State *L)
{
	struct lua_function_ctx *ctx = (struct lua_function_ctx *)
		lua_topointer(L, 1);
	struct request *request = ctx->request;
	struct obuf *out = ctx->out;
	struct obuf_svp *svp = &ctx->svp;
	lua_settop(L, 0); /* clear the stack to simplify the logic below */

	/* Compile expression */
	const char *expr = request->key;
	uint32_t expr_len = mp_decode_strl(&expr);
	if (luaL_loadbuffer(L, expr, expr_len, "=eval")) {
		diag_set(LuajitError, lua_tostring(L, -1));
		lbox_error(L);
	}

	/* Unpack arguments */
	const char *args = request->tuple;
	uint32_t arg_count = mp_decode_array(&args);
	luaL_checkstack(L, arg_count, "eval: out of stack");
	for (uint32_t i = 0; i < arg_count; i++) {
		luamp_decode(L, luaL_msgpack_default, &args);
	}

	/* Call compiled code */
	lua_call(L, arg_count, LUA_MULTRET);

	/* Send results of the called procedure to the client. */
	if (iproto_prepare_select(out, svp) != 0)
		diag_raise();
	ctx->out_is_dirty = true;
	struct mpstream stream;
	mpstream_init(&stream, out, obuf_reserve_cb, obuf_alloc_cb,
		      luamp_error, L);
	int nrets = lua_gettop(L);
	for (int k = 1; k <= nrets; ++k) {
		luamp_encode(L, luaL_msgpack_default, &stream, k);
	}
	mpstream_flush(&stream);
	iproto_reply_select(out, svp, request->header->sync, nrets);

	return 0;
}
Ejemplo n.º 14
0
int
field_map_builder_create(struct field_map_builder *builder,
			 uint32_t minimal_field_map_size,
			 struct region *region)
{
	builder->extents_size = 0;
	builder->slot_count = minimal_field_map_size / sizeof(uint32_t);
	if (minimal_field_map_size == 0) {
		builder->slots = NULL;
		return 0;
	}
	uint32_t sz = builder->slot_count * sizeof(builder->slots[0]);
	builder->slots = region_alloc(region, sz);
	if (builder->slots == NULL) {
		diag_set(OutOfMemory, sz, "region_alloc", "field_map");
		return -1;
	}
	memset((char *)builder->slots, 0, sz);
	builder->slots = builder->slots + builder->slot_count;
	return 0;
}
Ejemplo n.º 15
0
struct field_map_builder_slot_extent *
field_map_builder_slot_extent_new(struct field_map_builder *builder,
				  int32_t offset_slot, uint32_t multikey_count,
				  struct region *region)
{
	struct field_map_builder_slot_extent *extent;
	assert(!builder->slots[offset_slot].has_extent);
	uint32_t sz = sizeof(struct field_map_builder_slot_extent) +
		      multikey_count * sizeof(uint32_t);
	extent = region_alloc(region, sz);
	if (extent == NULL) {
		diag_set(OutOfMemory, sz, "region", "extent");
		return NULL;
	}
	memset(extent, 0, sz);
	extent->size = multikey_count;
	builder->slots[offset_slot].extent = extent;
	builder->slots[offset_slot].has_extent = true;
	builder->extents_size += sz;
	return extent;
}
Ejemplo n.º 16
0
struct ipc_value *
ipc_value_new()
{
	if (! mempool_is_initialized(&ipc_value_pool)) {
		/*
		 * We don't need to bother with
		 * destruction since the entire slab cache
		 * is freed when the thread ends.
		 */
		mempool_create(&ipc_value_pool, &cord()->slabc,
			       sizeof(struct ipc_value));
	}
	struct ipc_value *value = (struct ipc_value *)
		mempool_alloc(&ipc_value_pool);
	if (value == NULL) {
		diag_set(OutOfMemory, sizeof(struct ipc_value),
			 "ipc_msg_pool", "struct ipc_value");
		return NULL;
	}
	value->base.destroy = ipc_value_delete;
	return value;
}
Ejemplo n.º 17
0
int
opts_parse_key(void *opts, const struct opt_def *reg, const char *key,
	       uint32_t key_len, const char **data, uint32_t errcode,
	       uint32_t field_no, struct region *region,
	       bool skip_unknown_options)
{
	for (const struct opt_def *def = reg; def->name != NULL; def++) {
		if (key_len != strlen(def->name) ||
		    memcmp(key, def->name, key_len) != 0)
			continue;

		return opt_set(opts, def, data, region, errcode, field_no);
	}
	if (! skip_unknown_options) {
		char *errmsg = tt_static_buf();
		snprintf(errmsg, TT_STATIC_BUF_LEN, "unexpected option '%.*s'",
			 key_len, key);
		diag_set(ClientError, errcode, field_no, errmsg);
		return -1;
	}
	mp_next(data);
	return 0;
}
Ejemplo n.º 18
0
struct tuple *
luaT_tuple_new(struct lua_State *L, int idx, box_tuple_format_t *format)
{
	if (idx != 0 && !lua_istable(L, idx) && !luaT_istuple(L, idx)) {
		diag_set(IllegalParams, "A tuple or a table expected, got %s",
			 lua_typename(L, lua_type(L, idx)));
		return NULL;
	}

	struct ibuf *buf = tarantool_lua_ibuf;
	ibuf_reset(buf);
	struct mpstream stream;
	mpstream_init(&stream, buf, ibuf_reserve_cb, ibuf_alloc_cb,
		      luamp_error, L);
	if (idx == 0) {
		/*
		 * Create the tuple from lua stack
		 * objects.
		 */
		int argc = lua_gettop(L);
		mpstream_encode_array(&stream, argc);
		for (int k = 1; k <= argc; ++k) {
			luamp_encode(L, luaL_msgpack_default, &stream, k);
		}
	} else {
		/* Create the tuple from a Lua table. */
		luamp_encode_tuple(L, &tuple_serializer, &stream, idx);
	}
	mpstream_flush(&stream);
	struct tuple *tuple = box_tuple_new(format, buf->buf,
					    buf->buf + ibuf_used(buf));
	if (tuple == NULL)
		return NULL;
	ibuf_reinit(tarantool_lua_ibuf);
	return tuple;
}
Ejemplo n.º 19
0
/* we expect the form "host:port" */
int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
			   int xioflags, xiofile_t *xxfd,
			   unsigned groups, int socktype, int ipproto,
			   int pf) {
   struct single *xfd = &xxfd->stream;
   struct opt *opts0 = NULL;
   const char *hostname = argv[1], *portname = argv[2];
   bool dofork = false;
   union sockaddr_union us_sa,  *us = &us_sa;
   union sockaddr_union them_sa, *them = &them_sa;
   socklen_t uslen = sizeof(us_sa);
   socklen_t themlen = sizeof(them_sa);
   bool needbind = false;
   bool lowport = false;
   int level;
   int result;

   if (argc != 3) {
      Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1);
   }

   xfd->howtoend = END_SHUTDOWN;

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_FORK, &dofork);

   if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
			      xfd->para.socket.ip.res_opts[1],
			      xfd->para.socket.ip.res_opts[0],
			      them, &themlen, us, &uslen, &needbind, &lowport,
			      socktype) != STAT_OK) {
      return STAT_NORETRY;
   }

   if (dofork) {
      xiosetchilddied();	/* set SIGCHLD handler */
   }

   if (xioopts.logopt == 'm') {
      Info("starting connect loop, switching to syslog");
      diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
   } else {
      Info("starting connect loop");
   }

   do {	/* loop over retries and forks */

#if WITH_RETRY
      if (xfd->forever || xfd->retry) {
	 level = E_INFO;
      } else
#endif /* WITH_RETRY */
	 level = E_ERROR;

      result =
	 _xioopen_connect(xfd,
			  needbind?(struct sockaddr *)us:NULL, uslen,
			  (struct sockaddr *)them, themlen,
			  opts, pf, socktype, ipproto, lowport, level);
      switch (result) {
      case STAT_OK: break;
#if WITH_RETRY
      case STAT_RETRYLATER:
      case STAT_RETRYNOW:
	 if (xfd->forever || xfd->retry) {
	    --xfd->retry;
	    if (result == STAT_RETRYLATER) {
	       Nanosleep(&xfd->intervall, NULL);
	    }
	    dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
	    continue;
	 }
	 return STAT_NORETRY;
#endif /* WITH_RETRY */
      default:
	  free(opts0);free(opts);
	 return result;
      }

#if WITH_RETRY
      if (dofork) {
	 pid_t pid;
	 int level = E_ERROR;
	 if (xfd->forever || xfd->retry) {
	    level = E_WARN;	/* most users won't expect a problem here,
				   so Notice is too weak */
	 }
	 while ((pid = xio_fork(false, level)) < 0) {
	    if (xfd->forever || --xfd->retry) {
	       Nanosleep(&xfd->intervall, NULL); continue;
	    }
	  free(opts0);
	    return STAT_RETRYLATER;
	 }

	 if (pid == 0) {	/* child process */
	    xfd->forever = false;  xfd->retry = 0;
	    break;
	 }

	 /* parent process */
	 Close(xfd->fd);
	 /* with and without retry */
	 Nanosleep(&xfd->intervall, NULL);
	 dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
	 continue;	/* with next socket() bind() connect() */
      } else
#endif /* WITH_RETRY */
      {
	 break;
      }
   } while (true);
   /* only "active" process breaks (master without fork, or child) */

   if ((result = _xio_openlate(xfd, opts)) < 0) {
	   free(opts0);free(opts);
      return result;
   }
   free(opts0);free(opts);
   return 0;
}
Ejemplo n.º 20
0
/* creates the listening socket, bind, applies options; waits for incoming
   connection, checks its source address and port. Depending on fork option, it
   may fork a subprocess.
   pf specifies the syntax expected for range option. In the case of generic
   socket it is 0 (expecting raw binary data), and the real pf can be obtained
   from us->af_family; for other socket types pf == us->af_family
   Returns 0 if a connection was accepted; with fork option, this is always in
   a subprocess!
   Other return values indicate a problem; this can happen in the master
   process or in a subprocess.
   This function does not retry. If you need retries, handle this in a
   loop in the calling function (and always provide the options...)
   After fork, we set the forever/retry of the child process to 0
   applies and consumes the following option:
   PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
   PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
   OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
   OPT_SOURCEPORT, OPT_LOWPORT, cloexec
 */
int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
		 struct opt *opts, int pf, int socktype, int proto, int level) {
   struct sockaddr sa;
   socklen_t salen;
   int backlog = 5;	/* why? 1 seems to cause problems under some load */
   char *rangename;
   bool dofork = false;
   int maxchildren = 0;
   char infobuff[256];
   char lisname[256];
   union sockaddr_union _peername;
   union sockaddr_union _sockname;
   union sockaddr_union *pa = &_peername;	/* peer address */
   union sockaddr_union *la = &_sockname;	/* local address */
   socklen_t pas = sizeof(_peername);	/* peer address size */
   socklen_t las = sizeof(_sockname);	/* local address size */
   int result;

   retropt_bool(opts, OPT_FORK, &dofork);

   if (dofork) {
      if (!(xioflags & XIO_MAYFORK)) {
	 Error("option fork not allowed here");
	 return STAT_NORETRY;
      }
      xfd->flags |= XIO_DOESFORK;
   }

   retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);

   if (! dofork && maxchildren) {
       Error("option max-children not allowed without option fork");
       return STAT_NORETRY;
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;

   if (dofork) {
      xiosetchilddied();	/* set SIGCHLD handler */
   }

   if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
      return STAT_RETRYLATER;
   }

   applyopts_cloexec(xfd->fd, opts);

   applyopts(xfd->fd, opts, PH_PREBIND);
   applyopts(xfd->fd, opts, PH_BIND);
   if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
      Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
	   sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
	   strerror(errno));
      Close(xfd->fd);
      return STAT_RETRYLATER;
   }

#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
   }
#endif
   /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
      fields that we want to know. */
   salen = sizeof(sa);
   if (Getsockname(xfd->fd, us, &uslen) < 0) {
      Warn4("getsockname(%d, %p, {%d}): %s",
	    xfd->fd, &us, uslen, strerror(errno));
   }

   applyopts(xfd->fd, opts, PH_PASTBIND);
#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
   }
#endif /* WITH_UNIX */

#if WITH_IP4 /*|| WITH_IP6*/
   if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
      if (xioparserange(rangename, pf, &xfd->para.socket.range)
	  < 0) {
	 free(rangename);
	 return STAT_NORETRY;
      }
      free(rangename);
      xfd->para.socket.dorange = true;
   }
#endif

#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
   xio_retropt_tcpwrap(xfd, opts);
#endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */

#if WITH_TCP || WITH_UDP
   if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
      xfd->para.socket.ip.dosourceport = true;
   }
   retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
#endif /* WITH_TCP || WITH_UDP */

   applyopts(xfd->fd, opts, PH_PRELISTEN);
   retropt_int(opts, OPT_BACKLOG, &backlog);
   if (Listen(xfd->fd, backlog) < 0) {
      Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
      return STAT_RETRYLATER;
   }

   if (xioopts.logopt == 'm') {
      Info("starting accept loop, switching to syslog");
      diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
   } else {
      Info("starting accept loop");
   }
   while (true) {	/* but we only loop if fork option is set */
      char peername[256];
      char sockname[256];
      int ps;		/* peer socket */

      pa = &_peername;
      la = &_sockname;
      salen = sizeof(struct sockaddr);
      do {
	 /*? int level = E_ERROR;*/
	 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
	 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
	 if (ps >= 0) {
	    /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
	    break;	/* success, break out of loop */
	 }
	 if (errno == EINTR) {
	    continue;
	 }
	 if (errno == ECONNABORTED) {
	    Notice4("accept(%d, %p, {"F_socklen"}): %s",
		    xfd->fd, &sa, salen, strerror(errno));
	    continue;
	 }
	 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
	      xfd->fd, &sa, salen, strerror(errno));
	 Close(xfd->fd);
	 return STAT_RETRYLATER;
      } while (true);
      applyopts_cloexec(ps, opts);
      if (Getpeername(ps, &pa->soa, &pas) < 0) {
	 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
	       ps, pa, pas, strerror(errno));
	 pa = NULL;
      }
      if (Getsockname(ps, &la->soa, &las) < 0) {
	 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
	       ps, la, las, strerror(errno));
	 la = NULL;
      }
      Notice2("accepting connection from %s on %s",
	      pa?
	      sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
	      la?
	      sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");

      if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
	 if (Shutdown(ps, 2) < 0) {
	    Info2("shutdown(%d, 2): %s", ps, strerror(errno));
	 }
	 Close(ps);
	 continue;
      }

      if (pa != NULL)
	 Info1("permitting connection from %s",
	       sockaddr_info((struct sockaddr *)pa, pas,
			     infobuff, sizeof(infobuff)));

      if (dofork) {
	 pid_t pid;	/* mostly int; only used with fork */
         sigset_t mask_sigchld;

         /* we must prevent that the current packet triggers another fork;
            therefore we wait for a signal from the recent child: USR1
            indicates that is has consumed the last packet; CHLD means it has
            terminated */
         /* block SIGCHLD and SIGUSR1 until parent is ready to react */
         sigemptyset(&mask_sigchld);
         sigaddset(&mask_sigchld, SIGCHLD);
         Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);

	 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
	    Close(xfd->fd);
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
	    return STAT_RETRYLATER;
	 }
	 if (pid == 0) {	/* child */
	    pid_t cpid = Getpid();
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	    Info1("just born: child process "F_pid, cpid);
	    xiosetenvulong("PID", cpid, 1);

	    if (Close(xfd->fd) < 0) {
	       Info2("close(%d): %s", xfd->fd, strerror(errno));
	    }
	    xfd->fd = ps;

#if WITH_RETRY
	    /* !? */
	    xfd->forever = false;  xfd->retry = 0;
	    level = E_ERROR;
#endif /* WITH_RETRY */

	    break;
	 }

	 /* server: continue loop with listen */
	 /* shutdown() closes the socket even for the child process, but
	    close() does what we want */
	 if (Close(ps) < 0) {
	    Info2("close(%d): %s", ps, strerror(errno));
	 }

         /* now we are ready to handle signals */
         Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	 while (maxchildren) {
	    if (num_child < maxchildren) break;
	    Notice("maxchildren are active, waiting");
	    /* UINT_MAX would even be nicer, but Openindiana works only
	       with 31 bits */
	    while (!Sleep(INT_MAX)) ;	/* any signal lets us continue */
	 }
	 Info("still listening");
      } else {
	 if (Close(xfd->fd) < 0) {
	    Info2("close(%d): %s", xfd->fd, strerror(errno));
	 }
	 xfd->fd = ps;
	break;
      }
   }

   applyopts(xfd->fd, opts, PH_FD);
   applyopts(xfd->fd, opts, PH_PASTSOCKET);
   applyopts(xfd->fd, opts, PH_CONNECTED);
   if ((result = _xio_openlate(xfd, opts)) < 0)
      return result;

   /* set the env vars describing the local and remote sockets */
   if (la != NULL)  xiosetsockaddrenv("SOCK", la, las, proto);
   if (pa != NULL)  xiosetsockaddrenv("PEER", pa, pas, proto);

   return 0;
}
Ejemplo n.º 21
0
int
fiber_channel_put_msg_timeout(struct fiber_channel *ch,
			    struct ipc_msg *msg,
			    ev_tstamp timeout)
{
	/** Ensure delivery fairness in case of prolonged wait. */
	bool first_try = true;
	ev_tstamp start_time = ev_monotonic_now(loop());

	while (true) {
		/*
		 * Check if there is a ready reader first, and
		 * only if there is no reader try to put a message
		 * into the channel buffer.
		 */
		if (fiber_channel_has_readers(ch)) {
			/**
			 * There is a reader, push the message
			 * immediately.
			 */

			/*
			 * There can be no reader if there is
			 * a buffered message or the channel is
			 * closed.
			 */
			assert(ch->count == 0);
			assert(ch->is_closed == false);

			struct fiber *f = rlist_first_entry(&ch->waiters,
							    struct fiber,
							    state);
			/* Place the message on the pad. */
			f->wait_pad->msg = msg;

			fiber_channel_waiter_wakeup(f, FIBER_CHANNEL_WAIT_DONE);
			return 0;
		}
		if (ch->count < ch->size) {
			/*
			 * No reader, but the channel is buffered.
			 * Nice, drop the message in the buffer.
			 */

			/*
			 * Closed channels, are, well, closed,
			 * even if there is space in the buffer.
			 */
			if (ch->is_closed) {
				diag_set(ChannelIsClosed);
				return -1;
			}
			fiber_channel_buffer_push(ch, msg);
			return 0;
		}
		/**
		 * No reader and no space in the buffer.
		 * Have to wait.
		 */
		struct fiber *f = fiber();

		if (fiber_channel_check_wait(ch, start_time, timeout))
			return -1;

		/* Prepare a wait pad. */
		struct ipc_wait_pad pad;
		pad.status = FIBER_CHANNEL_WAIT_WRITER;
		pad.msg = msg;
		f->wait_pad = &pad;

		if (first_try) {
			rlist_add_tail_entry(&ch->waiters, f, state);
			first_try = false;
		} else {
			rlist_add_entry(&ch->waiters, f, state);
		}
		fiber_yield_timeout(timeout);
		/*
		 * In case of yield timeout, fiber->state
		 * is in the ch->waiters list, remove.
		 * rlist_del_entry() is a no-op if already done.
		 */
		rlist_del_entry(f, state);
		f->wait_pad = NULL;

		if (pad.status == FIBER_CHANNEL_WAIT_CLOSED) {
			/*
			 * The channel is closed.  Do not touch
			 * the channel object. It might be gone
			 * already.
			 */
			diag_set(ChannelIsClosed);
			return -1;
		}
		if (pad.status == FIBER_CHANNEL_WAIT_DONE)
			return 0;  /* OK, someone took the message. */
		timeout -= ev_monotonic_now(loop()) - start_time;
	}
}
Ejemplo n.º 22
0
static int
opt_set(void *opts, const struct opt_def *def, const char **val,
	struct region *region, uint32_t errcode, uint32_t field_no)
{
	int64_t ival;
	uint64_t uval;
	char *errmsg = tt_static_buf();
	double dval;
	uint32_t str_len;
	const char *str;
	char *ptr;
	char *opt = ((char *) opts) + def->offset;
	switch (def->type) {
	case OPT_BOOL:
		if (mp_typeof(**val) != MP_BOOL)
			goto type_mismatch_err;
		store_bool(opt, mp_decode_bool(val));
		break;
	case OPT_UINT32:
		if (mp_typeof(**val) != MP_UINT)
			goto type_mismatch_err;
		uval = mp_decode_uint(val);
		if (uval > UINT32_MAX)
			goto type_mismatch_err;
		store_u32(opt, uval);
		break;
	case OPT_INT64:
		if (mp_read_int64(val, &ival) != 0)
			goto type_mismatch_err;
		store_u64(opt, ival);
		break;
	case OPT_FLOAT:
		if (mp_read_double(val, &dval) != 0)
			goto type_mismatch_err;
		store_double(opt, dval);
		break;
	case OPT_STR:
		if (mp_typeof(**val) != MP_STR)
			goto type_mismatch_err;
		str = mp_decode_str(val, &str_len);
		str_len = MIN(str_len, def->len - 1);
		memcpy(opt, str, str_len);
		opt[str_len] = '\0';
		break;
	case OPT_STRPTR:
		if (mp_typeof(**val) != MP_STR)
			goto type_mismatch_err;
		str = mp_decode_str(val, &str_len);
		if (str_len > 0) {
			ptr = (char *) region_alloc(region, str_len + 1);
			if (ptr == NULL) {
				diag_set(OutOfMemory, str_len + 1, "region",
					 "opt string");
				return -1;
			}
			memcpy(ptr, str, str_len);
			ptr[str_len] = '\0';
			assert (strlen(ptr) == str_len);
		} else {
			ptr = NULL;
		}
		*(const char **)opt = ptr;
		break;
	case OPT_ENUM:
		if (mp_typeof(**val) != MP_STR)
			goto type_mismatch_err;
		str = mp_decode_str(val, &str_len);
		if (def->to_enum == NULL) {
			ival = strnindex(def->enum_strs, str, str_len,
					 def->enum_max);
		} else {
			ival = def->to_enum(str, str_len);
		}
		switch(def->enum_size) {
		case sizeof(uint8_t):
			store_u8(opt, (uint8_t)ival);
			break;
		case sizeof(uint16_t):
			store_u16(opt, (uint16_t)ival);
			break;
		case sizeof(uint32_t):
			store_u32(opt, (uint32_t)ival);
			break;
		case sizeof(uint64_t):
			store_u64(opt, (uint64_t)ival);
			break;
		default:
			unreachable();
		};
		break;
	case OPT_ARRAY:
		if (mp_typeof(**val) != MP_ARRAY)
			goto type_mismatch_err;
		ival = mp_decode_array(val);
		assert(def->to_array != NULL);
		if (def->to_array(val, ival, opt, errcode, field_no) != 0)
			return -1;
		break;
	case OPT_LEGACY:
		mp_next(val);
		break;
	default:
		unreachable();
	}
	return 0;

type_mismatch_err:
	snprintf(errmsg, TT_STATIC_BUF_LEN, "'%s' must be %s", def->name,
		 opt_type_strs[def->type]);
	diag_set(ClientError, errcode, field_no, errmsg);
	return -1;
}
Ejemplo n.º 23
0
int
fiber_channel_get_msg_timeout(struct fiber_channel *ch,
			    struct ipc_msg **msg,
			    ev_tstamp timeout)
{
	/** Ensure delivery fairness in case of prolonged wait. */
	bool first_try = true;
	ev_tstamp start_time = ev_monotonic_now(loop());

	while (true) {
		struct fiber *f;
		/*
		 * Buffered messages take priority over waiting
		 * fibers, if any, since they arrived earlier.
		 * Try to take a message from the buffer first.
		 */
		if (ch->count > 0) {
			/**
			 * There can't be any buffered stuff in
			 * a closed channel - everything is
			 * destroyed at close.
			 */
			assert(ch->is_closed == false);

			*msg = fiber_channel_buffer_pop(ch);

			if (fiber_channel_has_writers(ch)) {
				/*
				 * Move a waiting writer, if any,
				 * from the wait list to the tail
				 * the buffer, to preserve fairness
				 * in message delivery order.
				 */
				f = rlist_first_entry(&ch->waiters,
						      struct fiber,
						      state);
				fiber_channel_buffer_push(ch, f->wait_pad->msg);
				fiber_channel_waiter_wakeup(f,
					FIBER_CHANNEL_WAIT_DONE);
			}
			return 0;
		}
		if (fiber_channel_has_writers(ch)) {
			/**
			 * There is no buffered messages, *but*
			 * there is a writer. This is only
			 * possible when the channel is
			 * unbuffered.
			 * Take the message directly from the
			 * writer and be done with it.
			 */
			assert(ch->size == 0);
			f = rlist_first_entry(&ch->waiters,
					      struct fiber,
					      state);
			*msg = f->wait_pad->msg;
			fiber_channel_waiter_wakeup(f, FIBER_CHANNEL_WAIT_DONE);
			return 0;
		}
		if (fiber_channel_check_wait(ch, start_time, timeout))
			return -1;
		f = fiber();
		/**
		 * No reader and no space in the buffer.
		 * Have to wait.
		 */
		struct ipc_wait_pad pad;
		pad.status = FIBER_CHANNEL_WAIT_READER;
		f->wait_pad = &pad;
		if (first_try) {
			rlist_add_tail_entry(&ch->waiters, f, state);
			first_try = false;
		} else {
			rlist_add_entry(&ch->waiters, f, state);
		}
		fiber_yield_timeout(timeout);
		/*
		 * In case of yield timeout, fiber->state
		 * is in the ch->waiters list, remove.
		 * rlist_del_entry() is a no-op if already done.
		 */
		rlist_del_entry(f, state);
		f->wait_pad = NULL;
		if (pad.status == FIBER_CHANNEL_WAIT_CLOSED) {
			diag_set(ChannelIsClosed);
			return -1;
		}
		if (pad.status == FIBER_CHANNEL_WAIT_DONE) {
			*msg = pad.msg;
			return 0;
		}
		timeout -= ev_monotonic_now(loop()) - start_time;
	}
Ejemplo n.º 24
0
/**
 * A helper to find a Lua function by name and put it
 * on top of the stack.
 */
static int
box_lua_find(lua_State *L, const char *name, const char *name_end)
{
	int index = LUA_GLOBALSINDEX;
	int objstack = 0;
	const char *start = name, *end;

	while ((end = (const char *) memchr(start, '.', name_end - start))) {
		lua_checkstack(L, 3);
		lua_pushlstring(L, start, end - start);
		lua_gettable(L, index);
		if (! lua_istable(L, -1)) {
			diag_set(ClientError, ER_NO_SUCH_PROC,
				 name_end - name, name);
			lbox_error(L);
		}
		start = end + 1; /* next piece of a.b.c */
		index = lua_gettop(L); /* top of the stack */
	}

	/* box.something:method */
	if ((end = (const char *) memchr(start, ':', name_end - start))) {
		lua_checkstack(L, 3);
		lua_pushlstring(L, start, end - start);
		lua_gettable(L, index);
		if (! (lua_istable(L, -1) ||
			lua_islightuserdata(L, -1) || lua_isuserdata(L, -1) )) {
				diag_set(ClientError, ER_NO_SUCH_PROC,
					  name_end - name, name);
				lbox_error(L);
		}
		start = end + 1; /* next piece of a.b.c */
		index = lua_gettop(L); /* top of the stack */
		objstack = index;
	}


	lua_pushlstring(L, start, name_end - start);
	lua_gettable(L, index);
	if (!lua_isfunction(L, -1) && !lua_istable(L, -1)) {
		/* lua_call or lua_gettable would raise a type error
		 * for us, but our own message is more verbose. */
		diag_set(ClientError, ER_NO_SUCH_PROC,
			  name_end - name, name);
		lbox_error(L);
	}
	/* setting stack that it would contain only
	 * the function pointer. */
	if (index != LUA_GLOBALSINDEX) {
		if (objstack == 0) {        /* no object, only a function */
			lua_replace(L, 1);
		} else if (objstack == 1) { /* just two values, swap them */
			lua_insert(L, -2);
		} else {		    /* long path */
			lua_insert(L, 1);
			lua_insert(L, 2);
			objstack = 1;
		}
		lua_settop(L, 1 + objstack);
	}
	return 1 + objstack;
}
Ejemplo n.º 25
0
int main(int argc, const char *argv[]) {
   const char **arg1, *a;
   char *mainwaitstring;
   char buff[10];
   double rto;
   int i, argc0, result;
   struct utsname ubuf;
   int lockrc;

   if (mainwaitstring = getenv("SOCAT_MAIN_WAIT")) {
       sleep(atoi(mainwaitstring));
   }
   diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);

   /* we must init before applying options because env settings have lower
      priority and are to be overridden by options */
   if (xioinitialize(XIO_MAYALL) != 0) {
      Exit(1);
   }

   xiosetopt('p', "%");
   xiosetopt('o', ":");

   argc0 = argc;	/* save for later use */
   arg1 = argv+1;  --argc;
   while (arg1[0] && (arg1[0][0] == '-')) {
      switch (arg1[0][1]) {
      case 'V': socat_version(stdout); Exit(0);
#if WITH_HELP
      case '?':
      case 'h':
	 socat_usage(stdout);
	 xioopenhelp(stdout, (arg1[0][2]=='?'||arg1[0][2]=='h') ? (arg1[0][3]=='?'||arg1[0][3]=='h') ? 2 : 1 : 0);
	 Exit(0);
#endif /* WITH_HELP */
      case 'd': diag_set('d', NULL); break;
#if WITH_FILAN
      case 'D': xioparams->debug = true; break;
#endif
      case 'l':
	 switch (arg1[0][2]) {
	 case 'm': /* mixed mode: stderr, then switch to syslog; + facility */
	    diag_set('s', NULL);
	    xiosetopt('l', "m");
	    xioparams->logopt = arg1[0][2];
	    xiosetopt('y', &arg1[0][3]);
	    break;
	 case 'y': /* syslog + facility */
	    diag_set(arg1[0][2], &arg1[0][3]);
	    break;
	 case 'f': /* to file, +filename */
	 case 'p': /* artificial program name */
	    if (arg1[0][3]) {
	       diag_set(arg1[0][2], &arg1[0][3]);
	    } else if (arg1[1]) {
	       diag_set(arg1[0][2], arg1[1]);
	       ++arg1, --argc;
	    } else {
	       Error1("option -l%c requires an argument; use option \"-h\" for help", arg1[0][2]);
	    }
	    break;
	 case 's': /* stderr */
	    diag_set(arg1[0][2], NULL);
	    break;
	 case 'u':
	    diag_set('u', NULL);
	    break;
	 case 'h':
	    diag_set_int('h', true);
	    break;
	 default:
	    Error1("unknown log option \"%s\"; use option \"-h\" for help", arg1[0]);
	    break;
	 }
	 break;
      case 'v': xioparams->verbose = true; break;
      case 'x': xioparams->verbhex = true; break;
      case 'b': if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -b requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 xioparams->bufsiz = strtoul(a, (char **)&a, 0);
	 break;
      case 's':
	 diag_set_int('e', E_FATAL); break;
      case 't': if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -t requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 rto = strtod(a, (char **)&a);
	 xioparams->closwait.tv_sec = rto;
	 xioparams->closwait.tv_usec =
	    (rto-xioparams->closwait.tv_sec) * 1000000; 
	 break;
      case 'T':  if (arg1[0][2]) {
	    a = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((a = *arg1) == NULL) {
	       Error("option -T requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 rto = strtod(a, (char **)&a);
	 xioparams->total_timeout.tv_sec = rto;
	 xioparams->total_timeout.tv_usec =
	    (rto-xioparams->total_timeout.tv_sec) * 1000000; 
	 break;
      case 'u': xioparams->lefttoright = true; break;
      case 'U': xioparams->righttoleft = true; break;
      case 'g': xioopts_ignoregroups = true; break;
      case 'L': if (socat_opts.lock.lockfile)
	     Error("only one -L and -W option allowed");
	 if (arg1[0][2]) {
	    socat_opts.lock.lockfile = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((socat_opts.lock.lockfile = *arg1) == NULL) {
	       Error("option -L requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 break;
      case 'W': if (socat_opts.lock.lockfile)
	    Error("only one -L and -W option allowed");
	 if (arg1[0][2]) {
	    socat_opts.lock.lockfile = *arg1+2;
	 } else {
	    ++arg1, --argc;
	    if ((socat_opts.lock.lockfile = *arg1) == NULL) {
	       Error("option -W requires an argument; use option \"-h\" for help");
	       Exit(1);
	    }
	 }
	 socat_opts.lock.waitlock = true;
	 socat_opts.lock.intervall.tv_sec  = 1;
	 socat_opts.lock.intervall.tv_nsec = 0;
	 break;
#if WITH_IP4 || WITH_IP6
#if WITH_IP4
      case '4':
#endif
#if WITH_IP6
      case '6':
#endif
	 xioopts.default_ip = arg1[0][1];
	 xioopts.preferred_ip = arg1[0][1];
	 break;
#endif /* WITH_IP4 || WITH_IP6 */
      case 'c':
	 switch (arg1[0][2]) {
	 case 'S': xioparams->pipetype = XIOCOMM_SOCKETPAIRS; break;
	 case 'P':
	 case 'p': xioparams->pipetype = XIOCOMM_PIPES;       break;
	 case 's': xioparams->pipetype = XIOCOMM_SOCKETPAIR;  break;
	 case 'Y': xioparams->pipetype = XIOCOMM_PTYS;        break;
	 case 'y': xioparams->pipetype = XIOCOMM_PTY;         break;
	 case 't': xioparams->pipetype = XIOCOMM_TCP;         break;
	 case '0': case '1': case '2': case '3': case '4':
	 case '5': case '6': case '7': case '8': case '9':
	    xioparams->pipetype = atoi(&arg1[0][2]); break;
	 default: Error1("bad chain communication type \"%s\"", &arg1[0][2]);
	 }
	 break;
      case '\0':
      case '-':	/*! this is hardcoded "--" */
      case ',':
      case ':': break;	/* this "-" is a variation of STDIO or -- */
      default:
	 xioinqopt('p', buff, sizeof(buff));
	 if (arg1[0][1] == buff[0]) {
	    break;
	 }
	 Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]);
	 Exit(1);
      }
      /* the leading "-" might be a form of the first address */
      xioinqopt('p', buff, sizeof(buff));
      if (arg1[0][0] == '-' &&
	  (arg1[0][1] == '\0' || arg1[0][1] == ':' ||
	   arg1[0][1] == ',' || arg1[0][1] == '-'/*!*/ ||
	   arg1[0][1] == buff[0]))
	 break;
      ++arg1; --argc;
   }
#if 0
   Info1("%d address arguments", argc);
#else
   if (argc != 2) {
      Error1("exactly 2 addresses required (there are %d); use option \"-h\" for help", argc);
      Exit(1);
   }
#endif
   if (xioparams->lefttoright && xioparams->righttoleft) {
      Error("-U and -u must not be combined");
   }

   xioinitialize2();
   Info(copyright_socat);
#if WITH_OPENSSL
   Info(copyright_openssl);
   Info(copyright_ssleay);
#endif
   Debug2("socat version %s on %s", socatversion, timestamp);
   xiosetenv("VERSION", socatversion, 1);	/* SOCAT_VERSION */
   uname(&ubuf);	/* ! here we circumvent internal tracing (Uname) */
   Debug4("running on %s version %s, release %s, machine %s\n",
	   ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);

#if WITH_MSGLEVEL <= E_DEBUG
   for (i = 0; i < argc0; ++i) {
      Debug2("argv[%d]: \"%s\"", i, argv[i]);
   }
#endif /* WITH_MSGLEVEL <= E_DEBUG */

   /* not sure what signal should print a message */
   Signal(SIGHUP, socat_signal);
   Signal(SIGINT, socat_signal);
   Signal(SIGQUIT, socat_signal);
   Signal(SIGILL, socat_signal);
   /* SIGABRT for assert; catching caused endless recursion on assert in libc
      (tzfile.c:498: __tzfile_compute: Assertion 'num_types == 1' failed.) */
   /*Signal(SIGABRT, socat_signal);*/
   Signal(SIGBUS, socat_signal);
   Signal(SIGFPE, socat_signal);
   Signal(SIGSEGV, socat_signal);
   Signal(SIGTERM, socat_signal);
#if HAVE_SIGACTION
   {
      struct sigaction act;
      memset(&act, 0, sizeof(struct sigaction));
      act.sa_flags   = SA_NOCLDSTOP|SA_RESTART|SA_SIGINFO
#ifdef SA_NOMASK
	 |SA_NOMASK
#endif
	 ;
      act.sa_sigaction = xiosigaction_subaddr_ok;
      if (Sigaction(SIGUSR1, &act, NULL) < 0) {
	 /*! Linux man does not explicitely say that errno is defined */
	 Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
      }
   }
#else /* !HAVE_SIGACTION */
   if (Signal(SIGUSR1, xiosigaction_subaddr_ok) == SIG_ERR) {
      Warn1("signal(SIGCHLD, xiosigaction_subaddr_ok): %s", strerror(errno));
   }
#endif /* !HAVE_SIGACTION */

   /* set xio hooks */
   xiohook_newchild = &socat_newchild;

   if (lockrc = socat_lock()) {
      /* =0: goon; >0: locked; <0: error, printed in sub */
      if (lockrc > 0)
	 Error1("could not obtain lock \"%s\"", socat_opts.lock.lockfile);
      Exit(1);
   }

   Atexit(socat_unlock);

   result = socat(argc, arg1[0], arg1[1]);
   Notice1("exiting with status %d", result);
   Exit(result);
   return 0;	/* not reached, just for gcc -Wall */
}
static PT_THREAD(handle_bl(void))
{
	PT_BEGIN(&s.pt);
	s.retries = 3;
	do
	{
		flash_init();
		tftpc_init();
		tftpc_get(FIRMWARE_FILENAME);

		PT_WAIT_UNTIL(&s.pt, !tftpc_busy());

		uint8_t rc = tftpc_result();
		if(rc == TFTPC_FLASH_ERROR)
		{
			// any flash write errors, make sure the code doesn't run
			invalidate_code();
		}

		// valid app code in place?
		if(!check_valid_code())
		{
			// figure out what happened
			// and set indicator
			switch(rc) {
			case TFTPC_SUCCESS:
				diag_set(1);
				s.retries--;
				break;
			case TFTPC_SERVER_DOWN:
				diag_set(2);
				break;
			case TFTPC_FILE_NOT_FOUND:
				diag_set(3);
				break;
			case TFTPC_ERROR:
				diag_set(4);
				s.retries--;
				break;
			case TFTPC_FLASH_ERROR:
				diag_set(5);
				s.retries--;
				break;
			}

		}
		else
		{
			run_app();
		}

		timer_set(&s.timer, RETRY_TIME);
		PT_WAIT_UNTIL(&s.pt, timer_expired(&s.timer));
	} while (s.retries > 0);

	while(1)
	{
		PT_YIELD(&s.pt);
	}

	PT_END(&s.pt);
}