<commit_before>/**
 * @file net_backend_waitdevice.c
 * @author <NAME> <<EMAIL>>
 * 
 * @section LICENSE
 * 
 * This file is part of BadVPN.
 * 
 * BadVPN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 * 
 * BadVPN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * @section DESCRIPTION
 * 
 * Module which waits for the presence of a network interface.
 * 
 * Synopsis: net.backend.waitdevice(string ifname)
 */

#include <stdlib.h>
#include <string.h>

#include <ncd/NCDModule.h>
#include <ncd/NCDIfConfig.h>
#include <ncd/NCDInterfaceMonitor.h>

#include <generated/blog_channel_ncd_net_backend_waitdevice.h>

#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)

struct instance {
    NCDModuleInst *i;
    const char *ifname;
    NCDInterfaceMonitor monitor;
    int up;
};

static void monitor_handler (struct instance *o, const char *ifname, int if_flags)
{
    if (strcmp(ifname, o->ifname)) {
        return;
    }
    
    int was_up = o->up;
    o->up = !!(if_flags & NCDIFCONFIG_FLAG_EXISTS);
    
    if (o->up && !was_up) {
        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
    }
    else if (!o->up && was_up) {
        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
    }
}

static void func_new (NCDModuleInst *i)
{
    // allocate instance
    struct instance *o = malloc(sizeof(*o));
    if (!o) {
        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
        goto fail0;
    }
    NCDModuleInst_Backend_SetUser(i, o);
    
    // init arguments
    o->i = i;
    
    // check arguments
    NCDValue *arg;
    if (!NCDValue_ListRead(i->args, 1, &arg)) {
        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
        goto fail1;
    }
    if (NCDValue_Type(arg) != NCDVALUE_STRING) {
        ModuleLog(o->i, BLOG_ERROR, "wrong type");
        goto fail1;
    }
    o->ifname = NCDValue_StringValue(arg);
    
    // init monitor
    if (!NCDInterfaceMonitor_Init(&o->monitor, o->i->reactor, (NCDInterfaceMonitor_handler)monitor_handler, o)) {
        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
        goto fail1;
    }
    
    // query initial state
    o->up = !!(NCDIfConfig_query(o->ifname) & NCDIFCONFIG_FLAG_EXISTS);
    
    // signal up if needed
    if (o->up) {
        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
    }
    
    return;
    
fail1:
    free(o);
fail0:
    NCDModuleInst_Backend_SetError(i);
    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
}

static void func_die (void *vo)
{
    struct instance *o = vo;
    NCDModuleInst *i = o->i;
    
    // free monitor
    NCDInterfaceMonitor_Free(&o->monitor);
    
    // free instance
    free(o);
    
    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
}

static const struct NCDModule modules[] = {
    {
        .type = "net.backend.waitdevice",
        .func_new = func_new,
        .func_die = func_die
    }, {
        .type = NULL
    }
};

const struct NCDModuleGroup ncdmodule_net_backend_waitdevice = {
    .modules = modules
};
<commit_msg>ncd/modules/net_backend_waitdevice.c: port to NCDUdevManager<commit_after>/**
 * @file net_backend_waitdevice.c
 * @author <NAME> <<EMAIL>>
 * 
 * @section LICENSE
 * 
 * This file is part of BadVPN.
 * 
 * BadVPN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 * 
 * BadVPN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * @section DESCRIPTION
 * 
 * Module which waits for the presence of a network interface.
 * 
 * Synopsis: net.backend.waitdevice(string ifname)
 * Description: statement is UP when a network interface named ifname
 *   exists, and DOWN when it does not.
 */

#include <stdlib.h>
#include <string.h>

#include <misc/parse_number.h>
#include <ncd/NCDModule.h>
#include <ncd/NCDIfConfig.h>

#include <generated/blog_channel_ncd_net_backend_waitdevice.h>

#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)

struct instance {
    NCDModuleInst *i;
    const char *ifname;
    NCDUdevClient client;
    char *devpath;
    uintmax_t ifindex;
};

static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map)
{
    if (o->devpath && !strcmp(devpath, o->devpath) && !NCDUdevManager_Query(o->i->umanager, o->devpath)) {
        // free devpath
        free(o->devpath);
        
        // set no devpath
        o->devpath = NULL;
        
        // signal down
        NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
    } else {
        const BStringMap *cache_map = NCDUdevManager_Query(o->i->umanager, devpath);
        if (!cache_map) {
            goto out;
        }
        
        const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM");
        const char *interface = BStringMap_Get(cache_map, "INTERFACE");
        const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX");
        
        uintmax_t ifindex;
        if (!(subsystem && !strcmp(subsystem, "net") && interface && !strcmp(interface, o->ifname) && ifindex_str && parse_unsigned_integer(ifindex_str, &ifindex))) {
            goto out;
        }
        
        if (o->devpath && (strcmp(o->devpath, devpath) || o->ifindex != ifindex)) {
            // free devpath
            free(o->devpath);
            
            // set no devpath
            o->devpath = NULL;
            
            // signal down
            NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
        }
        
        if (!o->devpath) {
            // grab devpath
            o->devpath = devpath;
            devpath = NULL;
            
            // remember ifindex
            o->ifindex = ifindex;
            
            // signal up
            NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
        }
    }
    
out:
    free(devpath);
    if (have_map) {
        BStringMap_Free(&map);
    }
}

static void func_new (NCDModuleInst *i)
{
    // allocate instance
    struct instance *o = malloc(sizeof(*o));
    if (!o) {
        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
        goto fail0;
    }
    NCDModuleInst_Backend_SetUser(i, o);
    
    // init arguments
    o->i = i;
    
    // check arguments
    NCDValue *arg;
    if (!NCDValue_ListRead(i->args, 1, &arg)) {
        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
        goto fail1;
    }
    if (NCDValue_Type(arg) != NCDVALUE_STRING) {
        ModuleLog(o->i, BLOG_ERROR, "wrong type");
        goto fail1;
    }
    o->ifname = NCDValue_StringValue(arg);
    
    // init client
    NCDUdevClient_Init(&o->client, o->i->umanager, o, (NCDUdevClient_handler)client_handler);
    
    // set no devpath
    o->devpath = NULL;
    
    return;
    
fail1:
    free(o);
fail0:
    NCDModuleInst_Backend_SetError(i);
    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
}

static void func_die (void *vo)
{
    struct instance *o = vo;
    NCDModuleInst *i = o->i;
    
    // free devpath
    if (o->devpath) {
        free(o->devpath);
    }
    
    // free client
    NCDUdevClient_Free(&o->client);
    
    // free instance
    free(o);
    
    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
}

static const struct NCDModule modules[] = {
    {
        .type = "net.backend.waitdevice",
        .func_new = func_new,
        .func_die = func_die
    }, {
        .type = NULL
    }
};

const struct NCDModuleGroup ncdmodule_net_backend_waitdevice = {
    .modules = modules
};

{'fraction_non_alphanumeric': 0.11126826968411127, 'fraction_numerical': 0.004361150400754361, 'mean_word_length': 2.606034849128772, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2871089', 'n_tokens_mistral': 2826, 'n_tokens_neox': 2790, 'n_words': 715}