#include <CoreFoundation/CoreFoundation.h>
#include <OpenDirectory/OpenDirectory.h>
#include "macros.hpp"
#include "lib/common.hpp"
#include "lib/SmbConfig.hpp"
#include <sys/stat.h>
#include <sysexits.h>
#include <iostream>
static struct {
CFStringRef dsname;
CFTypeID cftype;
const char * smbname;
} share_params[] =
{
{ CFSTR("dsAttrTypeNative:name"), CFStringGetTypeID(),
"comment" },
{ CFSTR("dsAttrTypeNative:directory_path"), CFStringGetTypeID(),
"path" },
{ CFSTR("dsAttrTypeNative:smb_shared"), CFBooleanGetTypeID(),
"available" },
{ CFSTR("dsAttrTypeNative:smb_guestaccess"), CFBooleanGetTypeID(),
"guest ok" },
{ CFSTR("dsAttrTypeNative:smb_inherit_permissions"), CFBooleanGetTypeID(),
"inherit permissions" },
{ CFSTR("dsAttrTypeNative:smb_createmask"), CFStringGetTypeID(),
"create mask" },
{ CFSTR("dsAttrTypeNative:smb_directorymask"), CFStringGetTypeID(),
"directory mask" },
{ CFSTR("dsAttrTypeNative:smb_oplocks"), CFBooleanGetTypeID(),
"oplocks" },
{ CFSTR("dsAttrTypeNative:smb_strictlocking"), CFStringGetTypeID(),
"strict locking" },
};
bool
CopyFirstAttrFromRecord(CFStringRef * val,
ODRecordRef record,
CFStringRef attribute)
{
cf_typeref<CFArrayRef>
values(ODRecordCopyValues(record, attribute, NULL ));
if (!values || CFArrayGetCount(values) == 0) {
return false;
}
assert(CFArrayGetCount(values) >= 1);
*val = (CFStringRef)CFArrayGetValueAtIndex(values, 0);
CFRetain(*val);
return true;
}
bool GetStringAttrFromRecord(std::string& str,
ODRecordRef record, CFStringRef attribute)
{
bool status = true;
CFStringRef attrval = NULL;
if (!CopyFirstAttrFromRecord(&attrval, record, attribute)) {
return false;
}
str = cfstring_convert(attrval);
if (str.size() == 0){
status = false;
}
safe_release(attrval);
return status;
}
static bool convert_to_bool(const std::string& strval)
{
if (strval == "1" || strval == "yes" || strval == "true") {
return true;
}
return false;
}
static void insert_share_record(SmbShares& shares, ODRecordRef r)
{
bool default_guest = true;
std::string name;
std::string path;
if (!GetStringAttrFromRecord(name, r, CFSTR("dsAttrTypeNative:smb_name"))) {
GetStringAttrFromRecord(name, r, CFSTR("dsAttrTypeNative:name"));
}
GetStringAttrFromRecord(path, r, CFSTR("dsAttrTypeNative:directory_path"));
if (name.size() == 0 || path.size() == 0) {
return;
}
for (unsigned i = 0;
i < (sizeof(share_params) / sizeof(share_params[0]));
++i) {
std::string val;
if (!GetStringAttrFromRecord(val, r, share_params[i].dsname)) {
continue;
}
if (share_params[i].cftype == CFBooleanGetTypeID()) {
shares.set_param(name,
make_smb_param(share_params[i].smbname,
convert_to_bool(val)));
} else {
shares.set_param(name,
make_smb_param(share_params[i].smbname, val));
}
if (strcmp(share_params[i].smbname, "guest ok") == 0) {
default_guest = false;
}
}
if (default_guest) {
shares.set_param(name, make_smb_param("guest ok", Options::DefaultGuest));
}
shares.set_param(name, make_smb_param("read only", false));
}
static CFArrayRef query_shares(void)
{
CFArrayRef result;
cf_typeref<ODQueryRef> q(ODQueryCreateWithNodeType(
kCFAllocatorDefault,
kODNodeTypeLocalNodes,
kODRecordTypeSharePoints,
NULL ,
kODMatchEqualTo ,
NULL ,
NULL ,
(CFIndex)-1 ,
NULL ));
if (!q) {
return NULL;
}
result = ODQueryCopyResults(q, false , NULL );
if (!result) {
return NULL;
}
if (CFArrayGetCount(result) == 0) {
safe_release(result);
return NULL;
}
return result;
}
static int cmd_list_pending(SmbShares& shares)
{
shares.format(std::cout);
return EX_OK;
}
static int cmd_sync_shares(SmbShares& shares)
{
SyncMutex mutex("/var/samba/shares.mutex");
if (!mutex || getuid() != 0) {
VERBOSE("only root can synchronize SMB shares\n");
return EX_NOPERM;
}
VERBOSE("rewriting SMB shares\n");
for (int tries = 5; tries; --tries) {
if (shares.writeback()) {
post_service_notification("sharepoints", NULL);
return EX_OK;
}
}
VERBOSE("failed to write new SMB configuration\n");
return EX_CANTCREAT;
}
int main(int argc, char * const * argv)
{
CFArrayRef result;
SmbShares shares;
setprogname("smb-sync-shares");
umask(0);
Options::parse_shares(argc, argv);
result = query_shares();
if (result) {
VERBOSE("synchronizing %u results\n",
(unsigned)CFArrayGetCount(result));
} else {
return EX_OSERR;
}
for (unsigned i = 0; i < (unsigned)CFArrayGetCount(result); ++i) {
ODRecordRef r;
r = (ODRecordRef)CFArrayGetValueAtIndex(result, i);
insert_share_record(shares, r);
}
safe_release(result);
switch(Options::Command) {
case Options::LIST_PENDING:
return cmd_list_pending(shares);
default:
ASSERT(Options::Command == Options::SYNC);
return cmd_sync_shares(shares);
}
return EX_OK;
}