#include "smb_server_prefs.h"
#include "macros.hpp"
#include "common.hpp"
#include "SmbConfig.hpp"
#include <sstream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#define LAUNCHD_PLIST(name) ("/System/Library/LaunchDaemons/" name ".plist")
static const char config_header[] =
"#\n"
"# Configuration options for smbd(8), nmbd(8) and winbindd(8).\n"
"#\n"
"# This file is automatically generated, DO NOT EDIT!\n"
"#\n";
template<> basic_config::param_type
make_smb_param<std::string>(const std::string& key, const std::string& val)
{
return SmbConfig::param_type(key, val);
}
template<> basic_config::param_type
make_smb_param<bool>(const std::string& key, const bool& val)
{
return make_smb_param(key, val ? "yes" : "no");
}
static void
load_smbconf_metadata(basic_config::paramlist_type& params)
{
int err;
std::string line;
RegularExpression regex;
std::ifstream smbconf(SMB_RUN_CONFIG_PATH);
if (!smbconf) {
return;
}
err = regex.compile("^# ([^ \t][^:]*)[ \t]*:[ \t]*([^ \t].*)[ \t]*$");
if (err != 0) {
VERBOSE("regex compilation error: %s\n", regex.errstring(err));
return;
}
while (std::getline(smbconf, line)) {
err = regex.match(line, 3);
if (err != 0 && err != RegularExpression::NOMATCH) {
VERBOSE("regex match error: %s\n", regex.errstring(err));
return;
}
if (err == RegularExpression::NOMATCH) {
DEBUGMSG("no match for line '%s'\n", line.c_str());
continue;
}
if (regex.get_matches().size() != 3) {
DEBUGMSG("expected 3 matches, but got %zd\n",
regex.get_matches().size());
continue;
}
params.push_back(make_smb_param(regex.get_matches()[1],
regex.get_matches()[2]));
VERBOSE("updated metadata param='%s' value='%s'\n",
params.back().first.c_str(),
params.back().second.c_str());
}
}
SmbConfig::SmbConfig()
: basic_config(SMB_RUN_CONFIG_PATH, SMB_RUN_TEMPLATE)
{
this->m_smbd = new LaunchService("smbd",
"org.samba.smbd", LAUNCHD_PLIST("smbd"));
this->m_nmbd = new LaunchService("nmbd",
"org.samba.nmbd", LAUNCHD_PLIST("nmbd"));
this->m_winbind = new LaunchService("winbindd",
"org.samba.winbindd", LAUNCHD_PLIST("org.samba.winbindd"));
this->m_smbd->required(false);
this->m_nmbd->required(false);
this->m_winbind->required(false);
this->m_smbconf.insert(std::make_pair(GLOBAL, paramlist_type()));
this->m_smbconf.insert(std::make_pair(PRINTER, paramlist_type()));
this->m_smbconf.insert(std::make_pair(HOMES, paramlist_type()));
this->m_smbconf.insert(std::make_pair(NETLOGON, paramlist_type()));
this->m_smbconf.insert(std::make_pair(PROFILES, paramlist_type()));
load_smbconf_metadata(this->m_lastmeta);
}
SmbConfig::~SmbConfig()
{
delete this->m_smbd;
delete this->m_nmbd;
delete this->m_winbind;
}
template <class T> static T
find_by_name(T begin, T end, const SmbConfig::param_type& param)
{
for (; begin != end; ++begin) {
if (begin->first == param.first) {
return begin;
}
}
return end;
}
void SmbConfig::set_param(section_type section, const param_type& param)
{
this->m_smbconf[section].push_back(param);
}
void SmbConfig::set_meta(const param_type& param)
{
this->m_metaconf.push_back(param);
}
bool SmbConfig::meta_changed(param_type& param) const
{
SmbConfig::paramlist_type::const_iterator last;
SmbConfig::paramlist_type::const_iterator current;
last = find_by_name(this->m_lastmeta.begin(), this->m_lastmeta.end(),
param);
if (last == this->m_lastmeta.end()) {
return true;
}
current = find_by_name(this->m_metaconf.begin(), this->m_metaconf.end(),
param);
if (last == this->m_metaconf.end()) {
return true;
}
VERBOSE("last='%s' current='%s'\n",
last->second.c_str(), current->second.c_str());
return current->second != last->second;
}
static void
format_paramlist(const char * section,
const basic_config::paramlist_type& params,
std::ostream& out)
{
basic_config::paramlist_type::const_iterator p;
std::ostringstream tmp;
out << "\n[" << section << "]\n";
for (p = params.begin(); p != params.end(); ++p) {
tmp.str("");
tmp << p->second;
if (tmp.str().size() == 0) {
continue;
}
out << "\t" << p->first << " = " << tmp.str() << "\n";
}
}
static void
format_metadata(const SmbConfig * smb,
const SmbConfig::paramlist_type& meta,
std::ostream& out)
{
SmbConfig::paramlist_type::const_iterator p;
for (p = meta.begin(); p != meta.end(); ++p) {
out << "# " << p->first << ": " << p->second << "\n";
}
out << "# " << "Services required:";
if (smb->SmbdService().required() ||
smb->NmbdService().required() ||
smb->WinbindService().required()) {
if (smb->SmbdService().required()) {
out << ' ' << smb->SmbdService().label();
}
if (smb->NmbdService().required()) {
out << ' ' << smb->NmbdService().label();
}
if (smb->WinbindService().required()) {
out << ' ' << smb->WinbindService().label();
}
} else {
out << " none";
}
out << "\n#\n";
}
static SmbConfig::paramlist_type::size_type
count(SmbConfig::smbconf_type::const_iterator params)
{
return params->second.size();
}
void SmbConfig::format(std::ostream& out) const
{
SmbConfig::smbconf_type::const_iterator params;
out << config_header;
format_metadata(this, this->m_metaconf, out);
params = this->m_smbconf.find(GLOBAL);
if (count(params) > 0) {
format_paramlist("global", params->second, out);
}
params = this->m_smbconf.find(NETLOGON);
if (count(params) > 0) {
format_paramlist("netlogon", params->second, out);
}
params = this->m_smbconf.find(PROFILES);
if (count(params) > 0) {
format_paramlist("profiles", params->second, out);
}
params = this->m_smbconf.find(HOMES);
if (count(params) > 0) {
format_paramlist("homes", params->second, out);
}
params = this->m_smbconf.find(PRINTER);
if (count(params) > 0) {
format_paramlist("printers", params->second, out);
}
out << "\n[global]\n";
out.flush();
}
void SmbShares::set_param(const std::string& share_name,
const param_type& param)
{
this->m_smbshares[share_name].push_back(param);
}
void SmbShares::format(std::ostream& out) const
{
out << config_header;
for (smbshares_type::const_iterator i = m_smbshares.begin();
i != m_smbshares.end(); ++i) {
format_paramlist(i->first.c_str(), i->second, out);
}
out << "\n[global]\n";
out.flush();
}
bool basic_config::writeback() const
{
bool ret = false;
int fd = -1;
char * runpath = ::strdup(m_ctmpl.c_str());
if (!runpath) {
throw std::runtime_error("out of memory");
}
fd = ::mkstemp(runpath);
if (fd == -1) {
VERBOSE("failed to create temp file: %s\n",
::strerror(errno));
goto done;
}
::fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
try {
std::ostringstream out;
size_t remain;
this->format(out);
remain = out.str().size();
while (remain) {
ssize_t err;
size_t begin = out.str().size() - remain;
err = write(fd, out.str().c_str() + begin, remain);
if (err == -1) {
VERBOSE("failed to write config file: %s\n",
strerror(errno));
if (errno != EAGAIN && errno != EINTR) {
break;
}
} else {
remain -= err;
}
}
fsync(fd);
} catch (...) {
VERBOSE("caught exception writing config file\n");
goto done;
}
if (::rename(runpath, m_cpath.c_str()) == -1) {
VERBOSE("failed to rename temp file: %s\n",
::strerror(errno));
goto done;
}
ret = true;
done:
::unlink(runpath);
::close(fd);
free(runpath);
return ret;
}