Wow, that takes me back. I look at that old solution I had and can't believe what I was thinking. I'm sure at the time it made sense. I think I was trying to find just one option or something for setting up a SMTP server to know where to route messages to with that DHCP option for smtp-servers.
Though the brute force parse the file still seems to work for me, it starts from the start of the file and parses each lease, and just works with the last one (i guess someday I had planned to do something when the lease changed)
But for now when I see how your modern example would want to invoke a dhclient hooks script that would want to consume several environment variables we would read from the dhclient leases, I now modified my old C++ tool/hack to handle multiple options on the parameter, and even to specify an alternate location of the dhclient leases file.
So now, if you save the snipped below as dhclient_lease_parse.cpp
Code:
/**
* dhclient lease parser by Travis Hein
* licensed under Apache License, Version 2.0
*/
#include <string>
#include <map>
#include <list>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string.h>
using namespace std;
string DEFAULT_LEASE_FILE="/var/state/dhcp/dhclient.leases";
// all the stuff that can occur in a lease
struct dhcp_lease {
// the name / value pairs for options for this lease
string interface;
string address;
map<string, list<string> > options;
string renew;
string rebind;
string expire;
dhcp_lease() {}
};
// see: http://www.hardforum.com/archive/index.php/t-979477.html
inline string replaceAll( const string& s, const string& f, const string& r ) {
if ( s.empty() || f.empty() || f == r || s.find(f) == string::npos ) {
return s;
}
ostringstream build_it;
size_t i = 0;
for ( size_t pos; ( pos = s.find( f, i ) ) != string::npos; ) {
build_it.write( &s[i], pos - i );
build_it << r;
i = pos + f.size();
}
if ( i != s.size() ) {
build_it.write( &s[i], s.size() - i );
}
return build_it.str();
}
// see: http://www.codepedia.com/1/CppToUpperCase
std::string StringToUpper(std::string myString)
{
const int length = myString.length();
for(int i=0; i!=length ; ++i)
{
myString[i] = std::toupper(myString[i]);
}
return myString;
}
/**
* get an option(s) from the last entry in the dhclient.leases file.
* When no parameters specified, defaults to fixed-address, domain-name-servers routers
*/
int main (int argc, char** argv) {
// because we don't know for sure how far up the "lease {" token will be.
// we just parse every lease in the file; the assumption being the leases file will be small.
list<dhcp_lease> leases;
// the options to extract and set as environment variables.
list<string> options;
string leasesFileName = DEFAULT_LEASE_FILE;
int order = 0;
--argc; ++argv;
for (int i = 0; i < argc; i++) {
if (argv[i][0] == '-') {
string sw = argv[i];
// allow a different leases file to be specified with -f <filename>
if (sw == "-f") {
leasesFileName = argv[++i];
}
}
else {
switch (++order) {
string option = argv[i];
options.push_back(option);
break;
}
}
} // for (parameters)
if (options.size() == 0) {
// use default of these options to fetch
options.push_back("fixed-address");
options.push_back("domain-name-servers");
options.push_back("routers");
}
// try to open the dhclient.leases file
ifstream leasesFile;
leasesFile.open(leasesFileName.c_str());
if (!leasesFile) {
cout << "Cannot open dhcp leases file: " << leasesFileName << "\n";
return 1;
}
string line = "";
char c;
bool inLease = false;
int leaseCount = 0;
dhcp_lease currentLease;
while (leasesFile) {
leasesFile.get(c);
switch (c) {
case '{':
inLease = true;
break;
case '}':
inLease = false;
leases.push_front(currentLease);
currentLease = dhcp_lease();
break;
case '\n':
if (inLease) {
if (line.length() > 0) {
int startOfs = line.find_first_not_of(" \t");
int endOfs = line.find_last_of(";");
// trim extra stuff off from line
line = line.substr(startOfs, endOfs-startOfs);
if (line.find("option") != string::npos ) {
// drop the option keyword
line = line.substr(7);
size_t ofs = line.find_first_of(" ");
// extract the option name
string key = line.substr(0, ofs);
// extract the comma delimited values
list<string> values;
line = line.substr(ofs+1);
while (line.length()) {
ofs = line.find_first_of(',');
string valStr;
if (ofs == string::npos) {
ofs = 0;
valStr = line;
line = "";
}
else {
valStr = line.substr(0, ofs);
line = line.substr(ofs+1);
}
values.push_back(valStr);
} // while
pair<string, list<string> > p(key,values);
currentLease.options.insert(p);
}
else if (line.find("fixed-address") != string::npos) {
// the fixed-address isn't really an option, but its something we usually want to read.
// so for convenience later on, we stuff this in as an option.
string valstr = line.substr(line.find("fixed-address") + 13);
// trim extra stuff off from line
int startOfs = valstr.find_first_not_of(" \t");
int endOfs = valstr.find_last_of(";");
valstr = valstr.substr(startOfs, endOfs-startOfs);
list<string> values;
values.push_back(valstr);
pair<string, list<string> > p("fixed-address", values);
currentLease.options.insert(p);
}
else if (line.find("renew") != string::npos) {
// just grab the stuff after token and save it as string into struct
currentLease.renew = line.substr(line.find("renew") + 5);
}
else if (line.find("rebind") != string::npos) {
currentLease.rebind = line.substr(line.find("rebind") + 6);
}
else if (line.find("expire") != string::npos) {
currentLease.expire = line.substr(line.find("expire") + 6);
}
// reset the line string
line = "";
}
} // if in a lease
break;
default:
if (inLease) {
line += c;
}
}
} // while lease file has values
// retreive the last lease in the file (tokenize entire contents by { and } delimiters
// (which is now the first one in our list of parsed leases)
if (leases.size() > 0) {
list<dhcp_lease>::iterator itr = leases.begin();
dhcp_lease lease = *itr;
// get the requested option(s)
list<string>::iterator optionsIterator = options.begin();
while (optionsIterator != options.end() ) {
string getOption = *optionsIterator;
map<string, list<string> >::iterator valItr = lease.options.find(getOption);
// the usual unix delimiter is ":". Usually we only have one value though.
if (valItr != lease.options.end()) {
pair<string, list<string> > p = *valItr;
list<string> valList = p.second;
list<string>::iterator itmItr = valList.begin();
// stuff this thing we were asked to get as an anvironment variable.
// transform the option name to env variable name
// - convert all "-" to "_"
// - uppercase the string
string envName = replaceAll(getOption, "-", "_");
envName = StringToUpper(envName);
string envStr = envName + "=" + *itmItr;
cout << envStr << ";" << endl;
// setting environment variables only works if we invoke child process ourselves
// char tmpstr[255] = "\0";
// strncpy(tmpstr, envStr.c_str(),255);
// putenv(tmpstr);
}
else {
// this option could not be found in the lease
}
optionsIterator++;
} // each option
}
}
then
g++ -o dhclient_lease_parse dhclient_lease_parse.cpp
then place this into the path (such as copy dhclient_lease_parse to /usr/local/bin/
for example, running dhclient_lease_parse with no arguments spits out
./dhclient_lease_parse -f /var/lib/dhcp3/dhclient-eth0.lease
FIXED_ADDRESS=192.168.1.100;
DOMAIN_NAME_SERVERS=192.168.1.1;
ROUTERS=192.168.1.1;
So this can be eval'd by a shell script,
and the invoking the script now becomes:
Code:
#!/bin/sh
# uses the dhclient_lease_parse
# build that with
# g++ -o dhclient_lease_parse dhclient_lease_parse.cpp
# and copy the dhclient_lease_parse to /usr/local/bin or put on the path
PARSER="/usr/local/bin/dhclient_lease_parse";
#PARSER="./dhclient_lease_parse";
FILE="/var/lib/dhcp/dhclient-eth0.leases";
#FILE="/var/lib/dhcp3/dhclient-eth0.lease";
# evaluate the output of the dhclint_lease_parser, which is VAR=aValue; formatted things.
eval "$(${PARSER} -f ${FILE} fixed-address domain-name-servers dhcp-server-identifier)"
# the following variables are set from the eval
echo "IPADDR : ${FIXED_ADDRESS}";
echo "NAMESERVER : ${DOMAIN_NAME_SERVERS}";
echo "DHCP_SERVER: ${DHCP_SERVER_IDENTIFIER}";
Hope that's a bit more helpful than the original posted utility.