#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <regex.h>
#include "prio.h"
#include "debug.h"
#include <unistd.h>
#include "structs.h"
//
// This prioritizer suits iSCSI needs, makes it possible to prefer one path.
//
// (It's a bit of a misnomer since supports the client side [eg. open-iscsi]
// instead of just "iet".)
//
// Usage:
// prio "iet"
// prio_args "preferredip=10.11.12.13"
//
// Uses /dev/disk/by-path to find the IP of the device.
// Assigns prio 20 (high) to the preferred IP and prio 10 (low) to the rest.
//
// by Olivier Lambert <lambert.olivier.gmail.com>
//
#define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, dev)
//
// name: find_regex
// @param string: string you want to search into
// @param regex: the pattern used
// @return result: string finded in string with regex, "none" if none
char *find_regex(char * string, char * regex)
{
int err;
regex_t preg;
err = regcomp(&preg, regex, REG_EXTENDED);
if (err == 0) {
int match;
size_t nmatch = 0;
regmatch_t *pmatch = NULL;
nmatch = preg.re_nsub;
pmatch = malloc(sizeof(*pmatch) * nmatch);
if (pmatch) {
match = regexec(&preg, string, nmatch, pmatch, 0);
regfree(&preg);
if (match == 0) {
char *result = NULL;
int start = pmatch[0].rm_so;
int end = pmatch[0].rm_eo;
size_t size = end - start;
result = malloc (sizeof(*result) * (size + 1));
if (result) {
strncpy(result, &string[start], size);
result[size] = '\0';
free(pmatch);
return result;
}
}
free(pmatch);
}
}
return NULL;
}
//
// name: inet_prio
// @param
// @return prio
int iet_prio(const char *dev, char * args)
{
char preferredip_buff[255] = "";
char *preferredip = &preferredip_buff[0];
// Phase 1 : checks. If anyone fails, return prio 0.
// check if args exists
if (!args) {
dc_log(0, "need prio_args with preferredip set");
return 0;
}
// check if args format is OK
if (sscanf(args, "preferredip=%s", preferredip) ==1) {}
else {
dc_log(0, "unexpected prio_args format");
return 0;
}
// check if ip is not too short
if (strlen(preferredip) <= 7) {
dc_log(0, "prio args: preferredip too short");
return 0;
}
// Phase 2 : find device in /dev/disk/by-path to match device/ip
DIR *dir_p;
struct dirent *dir_entry_p;
enum { BUFFERSIZE = 1024 };
char buffer[BUFFERSIZE];
char fullpath[BUFFERSIZE] = "/dev/disk/by-path/";
dir_p = opendir(fullpath);
// loop to find device in /dev/disk/by-path
while( NULL != (dir_entry_p = readdir(dir_p))) {
if (dir_entry_p->d_name[0] != '.') {
char path[BUFFERSIZE] = "/dev/disk/by-path/";
strcat(path,dir_entry_p->d_name);
ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1);
if (nchars != -1) {
char *device;
buffer[nchars] = '\0';
device = find_regex(buffer,"(sd[a-z]+)");
// if device parsed is the right one
if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) {
char *ip;
ip = find_regex(dir_entry_p->d_name,"([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
// if prefferedip and ip fetched matches
if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) {
// high prio
free(ip);
free(device);
closedir(dir_p);
return 20;
}
free(ip);
}
free(device);
}
else {
printf("error\n");
}
}
}
// nothing found, low prio
closedir(dir_p);
return 10;
}
int getprio(struct path * pp, char * args,
__attribute__((unused)) unsigned int timeout)
{
return iet_prio(pp->dev, args);
}