Esempio n. 1
0
/* //////////////////////////////////////////////////////////////////////////////////////
 * interfaces
 */
tb_object_reader_t* tb_object_xplist_reader()
{
    // the reader
    static tb_object_reader_t s_reader = {0};

    // init reader
    s_reader.read   = tb_object_xplist_reader_done;
    s_reader.probe  = tb_object_xplist_reader_probe;

    // init hooker
    s_reader.hooker = tb_hash_map_init(TB_HASH_MAP_BUCKET_SIZE_MICRO, tb_element_str(tb_false), tb_element_ptr(tb_null, tb_null));
    tb_assert_and_check_return_val(s_reader.hooker, tb_null);

    // hook reader 
    tb_hash_map_insert(s_reader.hooker, "date", tb_object_xplist_reader_func_date);
    tb_hash_map_insert(s_reader.hooker, "data", tb_object_xplist_reader_func_data);
    tb_hash_map_insert(s_reader.hooker, "array", tb_object_xplist_reader_func_array);
    tb_hash_map_insert(s_reader.hooker, "string", tb_object_xplist_reader_func_string);
    tb_hash_map_insert(s_reader.hooker, "integer", tb_object_xplist_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, "real", tb_object_xplist_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, "true", tb_object_xplist_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, "false", tb_object_xplist_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, "dict", tb_object_xplist_reader_func_dictionary);

    // ok
    return &s_reader;
}
Esempio n. 2
0
tb_void_t tb_thread_store_setp(tb_thread_store_data_t const* data)
{
    // enter lock
    tb_spinlock_enter(&g_lock);

    // get data
    if (g_store) tb_hash_map_insert(g_store, (tb_pointer_t)tb_thread_self(), data);

    // leave lock
    tb_spinlock_leave(&g_lock);
}
Esempio n. 3
0
tb_void_t tb_object_dictionary_insert(tb_object_ref_t object, tb_char_t const* key, tb_object_ref_t val)
{
    // check
    tb_object_dictionary_t* dictionary = tb_object_dictionary_cast(object);
    tb_assert_and_check_return(dictionary && dictionary->hash && key && val);

    // add
    tb_hash_map_insert(dictionary->hash, key, val);

    // refn--
    if (!dictionary->incr) tb_object_exit(val);
}
Esempio n. 4
0
/* //////////////////////////////////////////////////////////////////////////////////////
 * implementation
 */
static tb_bool_t tb_aiop_rtor_select_addo(tb_aiop_rtor_impl_t* rtor, tb_aioo_impl_t const* aioo)
{
    // check
    tb_aiop_rtor_select_impl_t* impl = (tb_aiop_rtor_select_impl_t*)rtor;
    tb_assert_and_check_return_val(impl && rtor->aiop && aioo && aioo->sock, tb_false);

    // the aiop
    tb_aiop_impl_t* aiop = rtor->aiop;
    tb_assert_and_check_return_val(aiop, tb_false);

    // check size
    tb_spinlock_enter(&impl->lock.hash);
    tb_size_t size = tb_hash_map_size(impl->hash);
    tb_spinlock_leave(&impl->lock.hash);
    tb_assert_and_check_return_val(size < FD_SETSIZE, tb_false);

    // add sock => aioo
    tb_bool_t ok = tb_false;
    tb_spinlock_enter(&impl->lock.hash);
    if (impl->hash) 
    {
        tb_hash_map_insert(impl->hash, aioo->sock, aioo);
        ok = tb_true;
    }
    tb_spinlock_leave(&impl->lock.hash);
    tb_assert_and_check_return_val(ok, tb_false);

    // the fd
    tb_long_t fd = ((tb_long_t)aioo->sock) - 1;

    // the code
    tb_size_t code = aioo->code;

    // enter
    tb_spinlock_enter(&impl->lock.pfds);

    // update fd max
    if (fd > (tb_long_t)impl->sfdm) impl->sfdm = (tb_size_t)fd;
    
    // init fds
    if (code & (TB_AIOE_CODE_RECV | TB_AIOE_CODE_ACPT)) FD_SET(fd, &impl->rfdi);
    if (code & (TB_AIOE_CODE_SEND | TB_AIOE_CODE_CONN)) FD_SET(fd, &impl->wfdi);

    // leave
    tb_spinlock_leave(&impl->lock.pfds);

    // spak it
    if (aiop->spak[0] && code) tb_socket_send(aiop->spak[0], (tb_byte_t const*)"p", 1);

    // ok?
    return ok;
}
Esempio n. 5
0
File: json.c Progetto: luxuan/tbox
tb_bool_t tb_object_json_reader_hook(tb_char_t type, tb_object_json_reader_func_t func)
{
    // check
    tb_assert_and_check_return_val(type && func, tb_false);

    // the reader
    tb_object_reader_t* reader = tb_object_reader_get(TB_OBJECT_FORMAT_JSON);
    tb_assert_and_check_return_val(reader && reader->hooker, tb_false);

    // hook it
    tb_hash_map_insert(reader->hooker, (tb_pointer_t)(tb_size_t)type, func);

    // ok
    return tb_true;
}
Esempio n. 6
0
tb_bool_t tb_object_xplist_reader_hook(tb_char_t const* type, tb_object_xplist_reader_func_t func)
{
    // check
    tb_assert_and_check_return_val(type && func, tb_false);

    // the reader
    tb_object_reader_t* reader = tb_object_reader_get(TB_OBJECT_FORMAT_XPLIST);
    tb_assert_and_check_return_val(reader && reader->hooker, tb_false);

    // hook it
    tb_hash_map_insert(reader->hooker, type, func);

    // ok
    return tb_true;
}
Esempio n. 7
0
File: json.c Progetto: luxuan/tbox
/* //////////////////////////////////////////////////////////////////////////////////////
 * interfaces
 */
tb_object_reader_t* tb_object_json_reader()
{
    // the reader
    static tb_object_reader_t s_reader = {0};

    // init reader
    s_reader.read   = tb_object_json_reader_done;
    s_reader.probe  = tb_object_json_reader_probe;

    // init hooker
    s_reader.hooker = tb_hash_map_init(TB_HASH_MAP_BUCKET_SIZE_MICRO, tb_element_uint8(), tb_element_ptr(tb_null, tb_null));
    tb_assert_and_check_return_val(s_reader.hooker, tb_null);

    // hook reader 
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'n', tb_object_json_reader_func_null);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'N', tb_object_json_reader_func_null);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'[', tb_object_json_reader_func_array);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'\'', tb_object_json_reader_func_string);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'\"', tb_object_json_reader_func_string);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'0', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'1', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'2', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'3', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'4', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'5', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'6', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'7', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'8', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'9', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'.', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'-', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'+', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'e', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'E', tb_object_json_reader_func_number);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'t', tb_object_json_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'T', tb_object_json_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'f', tb_object_json_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'F', tb_object_json_reader_func_boolean);
    tb_hash_map_insert(s_reader.hooker, (tb_pointer_t)'{', tb_object_json_reader_func_dictionary);

    // ok
    return &s_reader;
}
Esempio n. 8
0
tb_size_t tb_hash_set_insert(tb_hash_set_ref_t self, tb_cpointer_t data)
{
    return tb_hash_map_insert((tb_hash_map_ref_t)self, data, tb_b2p(tb_true));
}
Esempio n. 9
0
static tb_pointer_t tb_thread_pool_worker_loop(tb_cpointer_t priv)
{
    // the worker
    tb_thread_pool_worker_t* worker = (tb_thread_pool_worker_t*)priv;

    // trace
    tb_trace_d("worker[%lu]: init", worker? worker->id : -1);

    // done
    do
    {
        // check
        tb_assert_and_check_break(worker && !worker->jobs && !worker->stats);

        // the pool
        tb_thread_pool_impl_t* impl = (tb_thread_pool_impl_t*)worker->pool;
        tb_assert_and_check_break(impl && impl->semaphore);

        // wait some time for leaving the lock
        tb_msleep((worker->id + 1)* 20);

        // init jobs
        worker->jobs = tb_vector_init(TB_THREAD_POOL_JOBS_WORKING_GROW, tb_element_ptr(tb_null, tb_null));
        tb_assert_and_check_break(worker->jobs);

        // init stats
        worker->stats = tb_hash_map_init(TB_HASH_MAP_BUCKET_SIZE_MICRO, tb_element_ptr(tb_null, tb_null), tb_element_mem(sizeof(tb_thread_pool_job_stats_t), tb_null, tb_null));
        tb_assert_and_check_break(worker->stats);
        
        // loop
        while (1)
        {
            // pull jobs if be idle
            if (!tb_vector_size(worker->jobs))
            {
                // enter 
                tb_spinlock_enter(&impl->lock);

                // init the pull time
                worker->pull = 0;

                // pull from the urgent jobs
                if (tb_list_entry_size(&impl->jobs_urgent))
                {
                    // trace
                    tb_trace_d("worker[%lu]: try pulling from urgent: %lu", worker->id, tb_list_entry_size(&impl->jobs_urgent));

                    // pull it
                    tb_remove_if_until(tb_list_entry_itor(&impl->jobs_urgent), tb_thread_pool_worker_walk_pull, worker);
                }

                // pull from the waiting jobs
                if (tb_list_entry_size(&impl->jobs_waiting))
                {
                    // trace
                    tb_trace_d("worker[%lu]: try pulling from waiting: %lu", worker->id, tb_list_entry_size(&impl->jobs_waiting));

                    // pull it
                    tb_remove_if_until(tb_list_entry_itor(&impl->jobs_waiting), tb_thread_pool_worker_walk_pull, worker);
                }

                // pull from the pending jobs and clean some finished and killed jobs
                if (tb_list_entry_size(&impl->jobs_pending))
                {
                    // trace
                    tb_trace_d("worker[%lu]: try pulling from pending: %lu", worker->id, tb_list_entry_size(&impl->jobs_pending));

                    // no jobs? try to pull from the pending jobs
                    if (!tb_vector_size(worker->jobs))
                        tb_remove_if(tb_list_entry_itor(&impl->jobs_pending), tb_thread_pool_worker_walk_pull_and_clean, worker);
                    // clean some finished and killed jobs
                    else tb_remove_if(tb_list_entry_itor(&impl->jobs_pending), tb_thread_pool_worker_walk_clean, worker);
                }

                // leave 
                tb_spinlock_leave(&impl->lock);

                // idle? wait it
                if (!tb_vector_size(worker->jobs))
                {
                    // killed?
                    tb_check_break(!tb_atomic_get(&worker->bstoped));

                    // trace
                    tb_trace_d("worker[%lu]: wait: ..", worker->id);

                    // wait some time
                    tb_long_t wait = tb_semaphore_wait(impl->semaphore, -1);
                    tb_assert_and_check_break(wait > 0);

                    // trace
                    tb_trace_d("worker[%lu]: wait: ok", worker->id);

                    // continue it
                    continue;
                }
                else
                {
#ifdef TB_TRACE_DEBUG
                    // update the jobs urgent size
                    tb_size_t jobs_urgent_size = tb_list_entry_size(&impl->jobs_urgent);

                    // update the jobs waiting size
                    tb_size_t jobs_waiting_size = tb_list_entry_size(&impl->jobs_waiting);

                    // update the jobs pending size
                    tb_size_t jobs_pending_size = tb_list_entry_size(&impl->jobs_pending);

                    // trace
                    tb_trace_d("worker[%lu]: pull: jobs: %lu, time: %lu ms, waiting: %lu, pending: %lu, urgent: %lu", worker->id, tb_vector_size(worker->jobs), worker->pull, jobs_waiting_size, jobs_pending_size, jobs_urgent_size);
#endif
                }
            }

            // done jobs
            tb_for_all (tb_thread_pool_job_t*, job, worker->jobs)
            {
                // check
                tb_assert_and_check_continue(job && job->task.done);

                // the job state
                tb_size_t state = tb_atomic_fetch_and_pset(&job->state, TB_STATE_WAITING, TB_STATE_WORKING);
                
                // the job is waiting? work it
                if (state == TB_STATE_WAITING)
                {
                    // trace
                    tb_trace_d("worker[%lu]: done: task[%p:%s]: ..", worker->id, job->task.done, job->task.name);

                    // init the time
                    tb_hong_t time = tb_cache_time_spak();

                    // done the job
                    job->task.done((tb_thread_pool_worker_ref_t)worker, job->task.priv);

                    // computate the time
                    time = tb_cache_time_spak() - time;

                    // exists? update time and count
                    tb_size_t               itor;
                    tb_hash_map_item_ref_t  item = tb_null;
                    if (    ((itor = tb_hash_map_find(worker->stats, job->task.done)) != tb_iterator_tail(worker->stats))
                        &&  (item = (tb_hash_map_item_ref_t)tb_iterator_item(worker->stats, itor)))
                    {
                        // the stats
                        tb_thread_pool_job_stats_t* stats = (tb_thread_pool_job_stats_t*)item->data;
                        tb_assert_and_check_break(stats);

                        // update the done count
                        stats->done_count++;

                        // update the total time 
                        stats->total_time += time;
                    }
                    
                    // no item? add it
                    if (!item) 
                    {
                        // init stats
                        tb_thread_pool_job_stats_t stats = {0};
                        stats.done_count = 1;
                        stats.total_time = time;

                        // add stats
                        tb_hash_map_insert(worker->stats, job->task.done, &stats);
                    }

#ifdef TB_TRACE_DEBUG
                    tb_size_t done_count = 0;
                    tb_hize_t total_time = 0;
                    tb_thread_pool_job_stats_t* stats = (tb_thread_pool_job_stats_t*)tb_hash_map_get(worker->stats, job->task.done);
                    if (stats)
                    {
                        done_count = stats->done_count;
                        total_time = stats->total_time;
                    }

                    // trace
                    tb_trace_d("worker[%lu]: done: task[%p:%s]: time: %lld ms, average: %lld ms, count: %lu", worker->id, job->task.done, job->task.name, time, (total_time / (tb_hize_t)done_count), done_count);
#endif

                    // update the job state
                    tb_atomic_set(&job->state, TB_STATE_FINISHED);
                }
                // the job is killing? work it
                else if (state == TB_STATE_KILLING)
                {
                    // update the job state
                    tb_atomic_set(&job->state, TB_STATE_KILLED);
                }
            }

            // clear jobs
            tb_vector_clear(worker->jobs);
        }

    } while (0);

    // exit worker
    if (worker)
    {
        // trace
        tb_trace_d("worker[%lu]: exit", worker->id);

        // stoped
        tb_atomic_set(&worker->bstoped, 1);

        // exit all private data
        tb_size_t i = 0;
        tb_size_t n = tb_arrayn(worker->priv);
        for (i = 0; i < n; i++)
        {
            // the private data
            tb_thread_pool_worker_priv_t* priv = &worker->priv[n - i - 1];

            // exit it
            if (priv->exit) priv->exit((tb_thread_pool_worker_ref_t)worker, priv->priv);

            // clear it
            priv->exit = tb_null;
            priv->priv = tb_null;
        }

        // exit stats
        if (worker->stats) tb_hash_map_exit(worker->stats);
        worker->stats = tb_null;

        // exit jobs
        if (worker->jobs) tb_vector_exit(worker->jobs);
        worker->jobs = tb_null;
    }

    // exit
    tb_thread_return(tb_null);
    return tb_null;
}
Esempio n. 10
0
static tb_void_t tb_ifaddrs_interface_done_hwaddr(tb_list_ref_t interfaces, tb_hash_map_ref_t names, struct nlmsghdr* response)
{
    // check
    tb_assert_and_check_return(interfaces && names && response);

    // the info
    struct ifaddrmsg* info = (struct ifaddrmsg *)NLMSG_DATA(response);

    // attempt to find the interface name
    tb_bool_t   owner = tb_false;
    tb_char_t*  name = (tb_char_t*)tb_hash_map_get(names, tb_u2p(info->ifa_index));
    if (!name)
    {
        // get the interface name
        struct rtattr*  rta = tb_null;
        tb_size_t       rta_size = NLMSG_PAYLOAD(response, sizeof(struct ifaddrmsg));
        for(rta = IFLA_RTA(info); RTA_OK(rta, rta_size); rta = RTA_NEXT(rta, rta_size))
        {
            // done
            tb_pointer_t    rta_data = RTA_DATA(rta);
            tb_size_t       rta_data_size = RTA_PAYLOAD(rta);
            switch(rta->rta_type)
            {
                case IFLA_IFNAME:
                    {
                        // make name
                        name = (tb_char_t*)tb_ralloc(name, rta_data_size + 1);
                        tb_assert_and_check_break(name);

                        // copy name
                        tb_strlcpy(name, rta_data, rta_data_size + 1);

                        // save name
                        tb_hash_map_insert(names, tb_u2p(info->ifa_index), name);
                        owner = tb_true;
                    }
                    break;
                default:
                    break;
            }
        }
    }

    // check
    tb_check_return(name);

    // done
    struct rtattr*  rta = tb_null;
    tb_size_t       rta_size = NLMSG_PAYLOAD(response, sizeof(struct ifaddrmsg));
    for(rta = IFLA_RTA(info); RTA_OK(rta, rta_size); rta = RTA_NEXT(rta, rta_size))
    {
        /* attempt to get the interface from the cached interfaces
         * and make a new interface if no the cached interface
         */
        tb_ifaddrs_interface_t      interface_new = {0};
        tb_ifaddrs_interface_ref_t  interface = tb_ifaddrs_interface_find((tb_iterator_ref_t)interfaces, name);
        if (!interface) interface = &interface_new;

        // check
        tb_assert(interface == &interface_new || interface->name);

        // done
        tb_pointer_t    rta_data = RTA_DATA(rta);
        tb_size_t       rta_data_size = RTA_PAYLOAD(rta);
        switch(rta->rta_type)
        {
            case IFLA_ADDRESS:
                {
                    // no hwaddr?
                    if (!(interface->flags & TB_IFADDRS_INTERFACE_FLAG_HAVE_HWADDR))
                    {
                        // check
                        tb_check_break(rta_data_size == sizeof(interface->hwaddr.u8));

                        // save flags
                        interface->flags |= TB_IFADDRS_INTERFACE_FLAG_HAVE_HWADDR;
                        if (info->ifa_flags & IFF_LOOPBACK) interface->flags |= TB_IFADDRS_INTERFACE_FLAG_IS_LOOPBACK;

                        // save hwaddr
                        tb_memcpy(interface->hwaddr.u8, rta_data, sizeof(interface->hwaddr.u8));

                        // trace
                        tb_trace_d("name: %s, hwaddr: %{hwaddr}", name, &interface->hwaddr);

                        // new interface? save it
                        if (interface == &interface_new)
                        {
                            // save interface name
                            interface->name = tb_strdup(name);
                            tb_assert(interface->name);

                            // save interface
                            tb_list_insert_tail(interfaces, interface);
                        }
                    }
                }
                break;
            case IFLA_IFNAME:
            case IFLA_BROADCAST:
            case IFLA_STATS:
                break;
            default:
                break;
        }
    }

    // exit name
    if (name && owner) tb_free(name);
    name = tb_null;
}
Esempio n. 11
0
static tb_void_t tb_ifaddrs_interface_done_ipaddr(tb_list_ref_t interfaces, tb_hash_map_ref_t names, struct nlmsghdr* response)
{
    // check
    tb_assert_and_check_return(interfaces && names && response);

    // the info
    struct ifaddrmsg* info = (struct ifaddrmsg *)NLMSG_DATA(response);

    // must be not link 
    tb_assert_and_check_return(info->ifa_family != AF_PACKET);

    // attempt to find the interface name
    tb_bool_t   owner = tb_false;
    tb_char_t*  name = (tb_char_t*)tb_hash_map_get(names, tb_u2p(info->ifa_index));
    if (!name)
    {
        // get the interface name
        struct rtattr*  rta = tb_null;
        tb_size_t       rta_size = NLMSG_PAYLOAD(response, sizeof(struct ifaddrmsg));
        for(rta = IFA_RTA(info); RTA_OK(rta, rta_size); rta = RTA_NEXT(rta, rta_size))
        {
            // done
            tb_pointer_t    rta_data = RTA_DATA(rta);
            tb_size_t       rta_data_size = RTA_PAYLOAD(rta);
            switch(rta->rta_type)
            {
                case IFA_LABEL:
                    {
                        // make name
                        name = (tb_char_t*)tb_ralloc(name, rta_data_size + 1);
                        tb_assert_and_check_break(name);

                        // copy name
                        tb_strlcpy(name, rta_data, rta_data_size + 1);

                        // save name
                        tb_hash_map_insert(names, tb_u2p(info->ifa_index), name);
                        owner = tb_true;
                    }
                    break;
                default:
                    break;
            }
        }
    }

    // check
    tb_check_return(name);

    // done
    struct rtattr*  rta = tb_null;
    tb_size_t       rta_size = NLMSG_PAYLOAD(response, sizeof(struct ifaddrmsg));
    for(rta = IFA_RTA(info); RTA_OK(rta, rta_size); rta = RTA_NEXT(rta, rta_size))
    {
        /* attempt to get the interface from the cached interfaces
         * and make a new interface if no the cached interface
         */
        tb_ifaddrs_interface_t      interface_new = {0};
        tb_ifaddrs_interface_ref_t  interface = tb_ifaddrs_interface_find((tb_iterator_ref_t)interfaces, name);
        if (!interface) interface = &interface_new;

        // check
        tb_assert(interface == &interface_new || interface->name);

        // done
        tb_pointer_t rta_data = RTA_DATA(rta);
        switch(rta->rta_type)
        {
            case IFA_LOCAL:
            case IFA_ADDRESS:
                {
                    // make ipaddr
                    tb_ipaddr_t ipaddr;
                    if (!tb_ifaddrs_netlink_ipaddr_save(&ipaddr, info->ifa_family, info->ifa_index, rta_data)) break;

                    // save flags
                    if ((info->ifa_flags & IFF_LOOPBACK) || tb_ipaddr_ip_is_loopback(&ipaddr)) 
                        interface->flags |= TB_IFADDRS_INTERFACE_FLAG_IS_LOOPBACK;

                    // save ipaddr
                    switch (tb_ipaddr_family(&ipaddr))
                    {
                    case TB_IPADDR_FAMILY_IPV4:
                        {
                            interface->flags |= TB_IFADDRS_INTERFACE_FLAG_HAVE_IPADDR4;
                            interface->ipaddr4 = ipaddr.u.ipv4;
                        }
                        break;
                    case TB_IPADDR_FAMILY_IPV6:
                        {
                            interface->flags |= TB_IFADDRS_INTERFACE_FLAG_HAVE_IPADDR6;
                            interface->ipaddr6 = ipaddr.u.ipv6;
                        }
                        break;
                    default:
                        break;
                    }

                    // trace
                    tb_trace_d("name: %s, ipaddr: %{ipaddr}", name, &ipaddr);

                    // new interface? save it
                    if (tb_ipaddr_family(&ipaddr) && interface == &interface_new)
                    {
                        // save interface name
                        interface->name = tb_strdup(name);
                        tb_assert(interface->name);

                        // save interface
                        tb_list_insert_tail(interfaces, interface);
                    }
                }
                break;
            case IFA_LABEL:
            case IFA_BROADCAST:
                break;
            default:
                break;
        }
    }

    // exit name
    if (name && owner) tb_free(name);
    name = tb_null;
}