TError TContext::Destroy() { TError error; if (NetEvt) NetEvt->Disconnect(); { auto holder_lock = Cholder->ScopedLock(); Cholder->DestroyRoot(holder_lock); Vholder->Destroy(); } error = Storage->Destroy(); if (error) L_ERR() << "Can't destroy key-value storage: " << error << std::endl; error = VolumeStorage->Destroy(); if (error) L_ERR() << "Can't destroy volume key-value storage: " << error << std::endl; error = Net->Destroy(); if (error) L_ERR() << "Can't destroy network: " << error << std::endl; return TError::Success(); }
ssize_t hncp_io_sendto(hncp o, void *buf, size_t len, const char *ifname, const struct in6_addr *to) { int flags = 0; struct sockaddr_in6 dst; ssize_t r; memset(&dst, 0, sizeof(dst)); if (!(dst.sin6_scope_id = if_nametoindex(ifname))) { L_ERR("unable to send on %s - if_nametoindex: %s", ifname, strerror(errno)); return -1; } dst.sin6_family = AF_INET6; dst.sin6_port = htons(HNCP_PORT); dst.sin6_addr = *to; r = sendto(o->udp_socket, buf, len, flags, (struct sockaddr *)&dst, sizeof(dst)); #if L_LEVEL >= 3 if (r < 0) { char buf[128]; const char *c = inet_ntop(AF_INET6, to, buf, sizeof(buf)); L_ERR("unable to send to %s%%%s - sendto:%s", c ? c : "?", ifname, strerror(errno)); } #endif /* L_LEVEL >= 3 */ return r; }
TError TNetwork::OpenLinks(std::vector<std::shared_ptr<TNlLink>> &links) { std::vector<std::string> devices; for (auto &device : config().network().devices()) devices.push_back(device); if (!Nl) { Nl = std::make_shared<TNl>(); if (!Nl) throw std::bad_alloc(); TError error = Nl->Connect(); if (error) { L_ERR() << "Can't open link: " << error << std::endl; return error; } } else { TError error = Nl->RefillCache(); if (error) { L_ERR() << "Can't refill link cache: " << error << std::endl; return error; } } if (!devices.size()) { TError error = Nl->GetDefaultLink(devices); if (error) { L_ERR() << "Can't open link: " << error << std::endl; return error; } } std::map<std::string, std::string> aliasMap; for (auto &alias : config().network().alias()) aliasMap[alias.iface()] = alias.name(); for (auto &name : devices) { auto l = std::make_shared<TNlLink>(Nl, name); if (!l) throw std::bad_alloc(); TError error = l->Load(); if (error) { L_ERR() << "Can't open link: " << error << std::endl; return error; } if (aliasMap.find(name) != aliasMap.end()) l->SetAlias(aliasMap.at(name)); links.push_back(l); } return TError::Success(); }
bool hncp_io_init(hncp o) { int s; int on = 1; int off = 0; #if 0 /* Could also use usock here; however, it uses getaddrinfo, which * doesn't seem to work when e.g. default routes aren't currently * set up. Too bad. */ char buf[6]; sprintf(buf, "%d", HNCP_PORT); s = usock(USOCK_IPV6ONLY|USOCK_UDP|USOCK_SERVER|USOCK_NONBLOCK, NULL, buf); if (s < 0) return false; #else struct sockaddr_in6 addr; s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (s<0) { L_ERR("unable to create IPv6 UDP socket"); return false; } fcntl(s, F_SETFL, O_NONBLOCK); memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(HNCP_PORT); const int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); if (bind(s, (struct sockaddr *)&addr, sizeof(addr))<0) { L_ERR("unable to bind to port %d", HNCP_PORT); return false; } #endif if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { L_ERR("unable to setsockopt IPV6_RECVPKTINFO:%s", strerror(errno)); return false; } if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)) < 0) { L_ERR("unable to setsockopt IPV6_MULTICAST_LOOP:%s", strerror(errno)); return false; } o->udp_socket = s; o->timeout.cb = _timeout; memset(&o->ufd, 0, sizeof(o->ufd)); o->ufd.fd = o->udp_socket; o->ufd.cb = _fd_callback; uloop_fd_add(&o->ufd, ULOOP_READ); return true; }
bool TContainerHolder::RestoreFromStorage() { std::vector<std::shared_ptr<TKeyValueNode>> nodes; auto holder_lock = ScopedLock(); TError error = Storage->ListNodes(nodes); if (error) { L_ERR() << "Can't list key-value nodes: " << error << std::endl; return false; } auto name2node = SortNodes(nodes); bool restored = false; for (auto &pair : name2node) { auto node = pair.second; auto name = pair.first; L_ACT() << "Found " << name << " container in kvs" << std::endl; kv::TNode n; error = node->Load(n); if (error) continue; restored = true; error = Restore(holder_lock, name, n); if (error) { L_ERR() << "Can't restore " << name << ": " << error << std::endl; Statistics->RestoreFailed++; node->Remove(); continue; } // FIXME since v1.0 we need to cleanup kvalue nodes with old naming if (TKeyValueStorage::Get(n, P_RAW_NAME, name)) node->Remove(); } if (restored) { for (auto &c: Containers) { if (c.second->IsLostAndRestored()) { ScheduleCgroupSync(); break; } } } return restored; }
ssize_t hncp_io_recvfrom(hncp o, void *buf, size_t len, char *ifname, struct in6_addr *src, struct in6_addr *dst) { struct sockaddr_in6 srcsa; struct iovec iov = {buf, len}; unsigned char cmsg_buf[256]; struct msghdr msg = {&srcsa, sizeof(srcsa), &iov, 1, cmsg_buf, sizeof(cmsg_buf), 0}; ssize_t l; struct cmsghdr *h; struct in6_pktinfo *ipi6; l = recvmsg(o->udp_socket, &msg, MSG_DONTWAIT); if (l > 0) { *ifname = 0; *src = srcsa.sin6_addr; for (h = CMSG_FIRSTHDR(&msg); h ; h = CMSG_NXTHDR(&msg, h)) if (h->cmsg_level == IPPROTO_IPV6 && h->cmsg_type == IPV6_PKTINFO) { ipi6 = (struct in6_pktinfo *)CMSG_DATA(h); if (!if_indextoname(ipi6->ipi6_ifindex, ifname)) { *ifname = 0; L_ERR("unable to receive - if_indextoname:%s", strerror(errno)); } *dst = ipi6->ipi6_addr; } if (!*ifname) { L_ERR("unable to receive - no ifname"); return -1; } } else { *ifname = 0; if (l < 0 && errno != EWOULDBLOCK) { L_DEBUG("unable to receive - recvmsg:%s", strerror(errno)); } } return l; }
TError TNetwork::PrepareLink(std::shared_ptr<TNlLink> link) { // 1:0 qdisc // 1:2 default class 1:1 root class // (unclassified 1:3 container a, 1:4 container b // traffic) 1:5 container a/c L() << "Prepare link " << link->GetAlias() << " " << link->GetIndex() << std::endl; TNlHtb qdisc(link, TcRootHandle(), rootHandle); if (!qdisc.Valid(defClass)) { (void)qdisc.Remove(); TError error = qdisc.Create(defClass); if (error) { L_ERR() << "Can't create root qdisc: " << error << std::endl; return error; } } TNlCgFilter filter(link, rootHandle, 1); if (filter.Exists()) (void)filter.Remove(); TError error = filter.Create(); if (error) { L_ERR() << "Can't create tc filter: " << error << std::endl; return error; } TNlClass tclass(link, rootHandle, defClass); uint64_t prio, rate, ceil; prio = config().network().default_prio(); rate = config().network().default_max_guarantee(); ceil = config().network().default_limit(); if (!tclass.Valid(prio, rate, ceil)) { (void)tclass.Remove(); TError error = tclass.Create(prio, rate, ceil); if (error) { L_ERR() << "Can't create default tclass: " << error << std::endl; return error; } } return TError::Success(); }
void sample(void) { int r; L_DEBUG("debug"); L_INFO("info"); L_NOTICE("notice"); L_WARN("warn"); L_ERR("err"); sput_fail_if(0, "0 isn't false!"); sput_fail_unless(1, "1 isn't true!"); /* Play with smock */ sput_fail_unless(smock_empty(), "smock empty"); smock_push_int("in", 1); sput_fail_unless(!smock_empty(), "smock not empty"); smock_push_int("out", 2); smock_push_int("in", 3); smock_push_int("out", 6); r = dummy_callback(1); sput_fail_unless(r == 2, "dummy_callback broken"); r = dummy_callback(3); sput_fail_unless(r == 6, "dummy_callback broken"); /* In the end, we should be again gone. */ sput_fail_unless(smock_empty(), "smock empty"); }
TError TCgroup::Remove() const { struct stat st; TError error; if (Secondary()) return TError(EError::Unknown, "Cannot create secondary cgroup " + Type()); L_ACT() << "Remove cgroup " << *this << std::endl; error = Path().Rmdir(); /* workaround for bad synchronization */ if (error && error.GetErrno() == EBUSY && !Path().StatStrict(st) && st.st_nlink == 2) { for (int i = 0; i < 100; i++) { usleep(config().daemon().cgroup_remove_timeout_s() * 10000); error = Path().Rmdir(); if (!error || error.GetErrno() != EBUSY) break; } } if (error && (error.GetErrno() != ENOENT || Exists())) L_ERR() << "Cannot remove cgroup " << *this << " : " << error << std::endl; return error; }
void DdCiAdapter::Write( const uint8_t *Buffer, int Length ) { if (Buffer && Length > 0) { if (safe_write( fd, Buffer, Length ) != Length) L_ERR( "can't write to CI adapter on device %d: %m", device->DeviceNumber() ); } }
TUintMap GetDefault() const override { TUintMap m; m["spawned"] = Statistics->Spawned; m["errors"] = Statistics->Errors; m["warnings"] = Statistics->Warns; m["master_uptime"] = (GetCurrentTimeMs() - Statistics->MasterStarted) / 1000; m["slave_uptime"] = (GetCurrentTimeMs() - Statistics->SlaveStarted) / 1000; m["queued_statuses"] = Statistics->QueuedStatuses; m["queued_events"] = Statistics->QueuedEvents; m["created"] = Statistics->Created; m["remove_dead"] = Statistics->RemoveDead; m["slave_timeout_ms"] = Statistics->SlaveTimeoutMs; m["rotated"] = Statistics->Rotated; m["restore_failed"] = Statistics->RestoreFailed; m["started"] = Statistics->Started; m["running"] = GetContainer()->GetRunningChildren(); uint64_t usage = 0; auto cg = MemorySubsystem.Cgroup(PORTO_DAEMON_CGROUP); TError error = MemorySubsystem.Usage(cg, usage); if (error) L_ERR() << "Can't get memory usage of portod" << std::endl; m["memory_usage_mb"] = usage / 1024 / 1024; m["epoll_sources"] = Statistics->EpollSources; m["containers"] = Statistics->Containers; m["volumes"] = Statistics->Volumes; m["clients"] = Statistics->Clients; return m; }
TError TContainerHolder::Restore(TScopedLock &holder_lock, const std::string &name, const kv::TNode &node) { if (name == ROOT_CONTAINER || name == PORTO_ROOT_CONTAINER) return TError::Success(); L_ACT() << "Restore container " << name << " (" << node.ShortDebugString() << ")" << std::endl; auto parent = GetParent(name); if (!parent) return TError(EError::InvalidValue, "invalid parent container"); int id = 0; TError error = RestoreId(node, id); if (error) return error; if (!id) return TError(EError::Unknown, "Couldn't restore container id"); auto c = std::make_shared<TContainer>(shared_from_this(), Storage, name, parent, id); error = c->Restore(holder_lock, node); if (error) { L_ERR() << "Can't restore container " << name << ": " << error << std::endl; return error; } Containers[name] = c; Statistics->Created++; return TError::Success(); }
bool TNlHtb::Valid(const TNlLink &link, uint32_t defaultClass) { int ret; struct nl_cache *qdiscCache; bool valid = true; ret = rtnl_qdisc_alloc_cache(link.GetSock(), &qdiscCache); if (ret < 0) { L_ERR() << "can't alloc qdisc cache" << std::endl; return false; } struct rtnl_qdisc *qdisc = rtnl_qdisc_get(qdiscCache, link.GetIndex(), Handle); if (qdisc) { link.Dump("found", qdisc); if (rtnl_tc_get_ifindex(TC_CAST(qdisc)) != link.GetIndex()) valid = false; else if (rtnl_tc_get_parent(TC_CAST(qdisc)) != Parent) valid = false; else if (rtnl_tc_get_handle(TC_CAST(qdisc)) != Handle) valid = false; else if (rtnl_tc_get_kind(TC_CAST(qdisc)) != std::string("htb")) valid = false; else if (rtnl_htb_get_defcls(qdisc) != TC_H_MIN(defaultClass)) valid = false; } else { valid = false; } rtnl_qdisc_put(qdisc); nl_cache_free(qdiscCache); return valid; }
void TContainerHolder::RemoveLeftovers() { TError error; for (auto hy: Hierarchies) { std::vector<TCgroup> cgroups; error = hy->Cgroup(PORTO_ROOT_CGROUP).ChildsAll(cgroups); if (error) L_ERR() << "Cannot dump porto " << hy->Type << " cgroups : " << error << std::endl; for (auto cg = cgroups.rbegin(); cg != cgroups.rend(); cg++) { std::string name = cg->Name.substr(strlen(PORTO_ROOT_CGROUP) + 1); if (Containers.count(name)) continue; if (!cg->IsEmpty()) (void)cg->KillAll(9); (void)cg->Remove(); } } for (auto it: Containers) { auto container = it.second; if (container->Prop->Get<bool>(P_WEAK)) { auto holder_lock = ScopedLock(); L_ACT() << "Destroy weak container " << it.first << std::endl; Destroy(holder_lock, container); } } }
void DdCiAdapter::Action() { LOG_FUNCTION_ENTER; if (ciSend.Start()) if (ciRecv.Start()) cCiAdapter::Action(); else { L_ERR( "couldn't start CAM TS Recv on device %d", device->DeviceNumber() ); ciSend.Cancel( 3 ); } else L_ERR( "couldn't start CAM TS Send on device %d", device->DeviceNumber() ); LOG_FUNCTION_EXIT; }
bool hncp_io_set_ifname_enabled(hncp o, const char *ifname, bool enabled) { struct ipv6_mreq val; val.ipv6mr_multiaddr = o->multicast_address; L_DEBUG("hncp_io_set_ifname_enabled %s %s", ifname, enabled ? "enabled" : "disabled"); if (!(val.ipv6mr_interface = if_nametoindex(ifname))) { L_DEBUG("unable to enable on %s - if_nametoindex: %s", ifname, strerror(errno)); goto fail; } if (setsockopt(o->udp_socket, IPPROTO_IPV6, enabled ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, (char *) &val, sizeof(val)) < 0) { L_ERR("unable to enable on %s - setsockopt:%s", ifname, strerror(errno)); goto fail; } /* Yay. It succeeded(?). */ return true; fail: return false; }
bool TNlCgFilter::Exists(const TNlLink &link) { int ret; struct nl_cache *clsCache; ret = rtnl_cls_alloc_cache(link.GetSock(), link.GetIndex(), Parent, &clsCache); if (ret < 0) { L_ERR() << "Can't allocate filter cache: " << nl_geterror(ret) << std::endl; return false; } link.LogCache(clsCache); struct CgFilterIter { uint32_t parent; uint32_t handle; bool exists; } data = { Parent, Handle, false }; nl_cache_foreach(clsCache, [](struct nl_object *obj, void *data) { CgFilterIter *p = (CgFilterIter *)data; if (rtnl_tc_get_handle(TC_CAST(obj)) == p->handle && rtnl_tc_get_parent(TC_CAST(obj)) == p->parent) p->exists = true; }, &data); nl_cache_free(clsCache); return data.exists; }
std::map<std::string, std::shared_ptr<TKeyValueNode>> TContainerHolder::SortNodes(const std::vector<std::shared_ptr<TKeyValueNode>> &nodes) { // FIXME since v1.0 we use container id as kvalue node name and because // we need to create containers in particular order we create this // name-sorted map std::map<std::string, std::shared_ptr<TKeyValueNode>> name2node; for (auto node : nodes) { std::string name; kv::TNode n; TError error = node->Load(n); if (!error) error = TKeyValueStorage::Get(n, P_RAW_NAME, name); if (error) { L_ERR() << "Can't load key-value node " << node->Name << ": " << error << std::endl; node->Remove(); Statistics->RestoreFailed++; continue; } name2node[name] = node; } return name2node; }
int conf_get_ifvalue(conf conf, iface i, enum conf_iface_value v) { if(v < CIFV_CONF_MAX && i->conf.state && i->conf.values[v] != INT_MIN) { return i->conf.values[v]; } if(v < CIFV_CONF_ARRAY_MAX) { return cifv_defaults[v]; } // The following values depend on other configuration values int res; switch (v) { case CIFV_PIM_TRIGGERED_HELLO_DELAY_MS: return conf_get_ifvalue(conf, i, CIFV_PIM_HELLO_PERIOD_MS) / 6; case CIFV_PIM_HOLDTIME_S: return ((res = ((3.5 * conf_get_ifvalue(conf, i, CIFV_PIM_HELLO_PERIOD_MS))/1000)))?res:1; case CIFV_PIM_US_JP_HOLDTIME_S: return ((res = ((3.5 * conf_get_ifvalue(conf, i, CIFV_PIM_US_T_PERIODIC_MS))/1000)))?res:1; default: L_ERR("Invalid configuration ifvalue was requested !! (%u)", (unsigned int)v); return INT_MIN; } return 0; }
/** * dup an object */ lv_t *lisp_dup_item(lv_t *v) { lv_t *r; lv_t *vptr = v; lv_t *rptr; assert(v); switch(v->type) { case l_int: r = lisp_create_int(0); mpz_set(L_INT(r), L_INT(v)); return r; case l_rational: r = lisp_create_rational(1, 1); mpq_set(L_RAT(r), L_RAT(v)); return r; case l_float: r = lisp_create_float(0.0); mpfr_set(L_FLOAT(r), L_FLOAT(v), MPFR_ROUND_TYPE); return r; case l_bool: return v; case l_sym: return lisp_create_symbol(L_SYM(v)); case l_str: return lisp_create_string(L_STR(v)); case l_null: return v; case l_port: /* can't really copy this -- it's a socket or a file handle, or something else. */ return v; case l_char: return lisp_create_char(L_CHAR(v)); case l_fn: /* can't really copy this either, but it's essentially immutable */ return v; case l_err: return lisp_create_err(L_ERR(v)); case l_hash: /* FIXME: should really be a copy */ return v; case l_pair: r = lisp_create_pair(NULL, NULL); rptr = r; while(vptr && L_CAR(vptr)) { L_CAR(rptr) = lisp_dup_item(L_CAR(vptr)); vptr = L_CDR(vptr); if(vptr) { L_CDR(rptr) = lisp_create_pair(NULL, NULL); rptr = L_CDR(rptr); } } return r; } assert(0); }
TError TCred::LoadGroups(const std::string &user) { if (FindGroups(user, Gid, Groups)) { L_ERR() << "Cannot load groups for " << user << std::endl; Groups.resize(1); Groups[0] = Gid; } return TError::Success(); }
bool DdCiAdapter::Reset( int Slot ) { ciRecv.ClrBuffer(); ciSend.ClrBuffer(); if (ioctl( fd, CA_RESET, 1 << Slot ) != -1) return true; else L_ERR( "can't reset CAM slot %d on device %d: %m", Slot, device->DeviceNumber() ); return false; }
TError TCgroup::Attach(pid_t pid) const { if (Secondary()) return TError(EError::Unknown, "Cannot attach to secondary cgroup " + Type()); L_ACT() << "Attach process " << pid << " to " << *this << std::endl; TError error = Knob("cgroup.procs").WriteAll(std::to_string(pid)); if (error) L_ERR() << "Cannot attach process " << pid << " to " << *this << " : " << error << std::endl; return error; }
uint64_t GetDefault() const override { auto cg = GetContainer()->GetCgroup(MemorySubsystem); uint64_t val; TError error = MemorySubsystem.Usage(cg, val); if (error) { L_ERR() << "Can't get memory usage: " << error << std::endl; return -1; } return val; }
void TContainerHolder::DestroyRoot(TScopedLock &holder_lock) { auto list = List(true); // we want children to be removed first std::reverse(std::begin(list), std::end(list)); for (auto c: list) { TError error = Destroy(holder_lock, c); if (error) L_ERR() << "Can't destroy container " << c->GetName() << ": " << error << std::endl; } }
TCred TCred::Current() { TCred cred(geteuid(), getegid()); cred.Groups.resize(getgroups(0, nullptr)); if (getgroups(cred.Groups.size(), cred.Groups.data()) < 0) { L_ERR() << "Cannot get supplementary groups for " << cred.Uid << std::endl; cred.Groups.resize(1); cred.Groups[0] = cred.Gid; } return cred; }
DdCiAdapter::DdCiAdapter( cDevice *dev, int ca_fd, int ci_fdw, int ci_fdr, cString &devNameCa, cString &devNameCi ) : device( dev ) , fd( ca_fd ) , caDevName( devNameCa ) , ciSend( *this, ci_fdw, devNameCi ) , ciRecv( *this, ci_fdr, devNameCi ) , camSlot( 0 ) { LOG_FUNCTION_ENTER; if (!dev) { L_ERROR_STR( "dev=NULL!" ); return; } SetDescription( "DDCI adapter on device %d (%s)", device->DeviceNumber(), *caDevName ); ca_caps_t Caps; if (ioctl( fd, CA_GET_CAP, &Caps ) == 0) { if ((Caps.slot_type & CA_CI_LINK) != 0) { int NumSlots = Caps.slot_num; if (NumSlots > 0) { for (int i = 0; i < NumSlots; i++) { if (!camSlot) { camSlot = new DdCiCamSlot( *this, ciSend ); } else { L_ERR( "Currently only ONE CAM slot supported" ); } } L_DBG( "DdCiAdapter(%s) for device %d created", *caDevName, device->DeviceNumber() ); Start(); } else L_ERR( "no CAM slots found on device %d", device->DeviceNumber() ); } else L_INF( "device %d doesn't support CI link layer interface", device->DeviceNumber() ); } else L_ERR( "can't get CA capabilities on device %d", device->DeviceNumber() ); LOG_FUNCTION_EXIT; }
int main(int argc, char *const argv[]) { const char *prog = argv[0]; int c, i; openlog("ohybridproxy", LOG_PERROR | LOG_PID, LOG_DAEMON); uloop_init(); while ((c = getopt(argc, argv, "46a:p:h")) != -1) { switch (c) { case '4': case '6': /* Ignored for now */ break; case 'a': bindaddr = optarg; break; case 'p': bindport = atoi(optarg); break; default: goto help; } } argc -= optind; argv += optind; if (argc == 0) { help: show_help(prog); return 1; } for (i = 0 ; i < argc ; i++) { char *ifname = argv[i]; char *domain = strchr(ifname, '='); if (!domain) { fprintf(stderr, "Invalid domain specification #%d (no =): %s", i, ifname); return 1; } *domain++ = 0; /* Now we can do stuff with ifname+domain. */ if (!d2m_add_interface(ifname, domain)) { L_ERR("Failed to add interface %s: %s", ifname, strerror(errno)); return 2; } } return io_run(bindaddr, bindport, MAXIMUM_REQUEST_DURATION_IN_MS); }
lv_t *lisp_create_type(void *value, lisp_type_t type) { lv_t *result; result = safe_malloc(sizeof(lv_t)); result->type = type; result->row = 0; result->col = 0; result->file = NULL; switch(type) { case l_char: L_CHAR(result) = *((char*)value); break; case l_int: mpz_init(L_INT(result)); mpz_set_si(L_INT(result), *(int64_t *)value); break; case l_rational: mpq_init(L_RAT(result)); break; case l_float: mpfr_init(L_FLOAT(result)); mpfr_set_d(L_FLOAT(result), *(double*)value, MPFR_ROUND_TYPE); break; case l_bool: L_BOOL(result) = *((int*)value); break; case l_sym: L_SYM(result) = safe_strdup((char*)value); break; case l_str: L_STR(result) = safe_strdup((char*)value); break; case l_err: L_ERR(result) = *((lisp_errsubtype_t *)value); break; case l_fn: L_FN(result) = (lisp_method_t)value; break; case l_port: L_PORT(result) = (port_info_t *)value; break; default: assert(0); fprintf(stderr, "Bad type"); exit(EXIT_FAILURE); } return result; }
/** * determine if an error object is a particular * subtype */ lv_t *c_error_type(lexec_t *exec, lv_t *v, lisp_errsubtype_t s) { assert(exec && v); assert(v->type == l_pair); rt_assert(c_list_length(v) == 1, le_arity, "expecting 1 arg"); lv_t *a0 = L_CAR(v); if((a0->type == l_err) && (L_ERR(a0) == s)) return lisp_create_bool(1); return lisp_create_bool(0); }