/* Copyright (C) 2000 Stefan Westerfeld stefan@space.twc.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mcoputils.h" #include "mcopconfig.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Arts; // 0: Dir ok // 1: Dir not ok static int check_tmp_dir(const char *tmp_dir) { int result; struct stat stat_buf; result = lstat(tmp_dir, &stat_buf); if ((result == -1) && (errno == ENOENT)) { result = mkdir(tmp_dir, 0700); if (result == -1) { arts_warning("Error: Cannot create directory \"%s\".\n", tmp_dir); return 1; } result = stat(tmp_dir, &stat_buf); } if ((result == -1) || (!S_ISDIR(stat_buf.st_mode))) { arts_warning("Error: \"%s\" is not a directory.\n", tmp_dir); return 1; } if (stat_buf.st_uid != getuid()) { arts_warning("Error: \"%s\" is owned by uid %d instead of uid %d.\n", tmp_dir, stat_buf.st_uid, getuid()); return 1; } return 0; } // 0: Link not ok // != 0: location of mcop directory static char *locate_mcop_dir() { struct passwd *pw_ent; string kde_tmp_dir; string user_tmp_dir; int uid = getuid(); const char *home_dir = getenv("HOME"); const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME"); const char *kde_prefix = "/socket-"; const char *tmp; char *tmp_buf; int result; struct stat stat_buf; tmp = getenv("KDETMP"); if (!tmp || !tmp[0]) tmp = getenv("TMPDIR"); if (!tmp || !tmp[0]) tmp = "/tmp"; kde_tmp_dir = ""; pw_ent = getpwuid(uid); if (!pw_ent) { arts_warning("Error: Can not find password entry for uid %d.\n", getuid()); return 0; } user_tmp_dir = string(tmp) + "/ksocket-" + string(pw_ent->pw_name); if (!kde_home || !kde_home[0]) { kde_home = "~/.kde/"; } if (kde_home[0] == '~') { if (uid == 0) { home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root"; } if (!home_dir || !home_dir[0]) { arts_fatal("Aborting. $HOME not set!"); } kde_home++; kde_tmp_dir = string(home_dir); } kde_tmp_dir += kde_home; /** Strip trailing '/' **/ if ( kde_tmp_dir[kde_tmp_dir.length()-1] == '/') kde_tmp_dir.resize(kde_tmp_dir.length()-1); result = stat(kde_tmp_dir.c_str(), &stat_buf); if (result == -1) { return 0; } kde_tmp_dir += kde_prefix; { char buf[1024]; if (gethostname(buf, sizeof(buf)-1) != 0) { arts_fatal("Aborting. Could not determine hostname or hostname too long."); } buf[sizeof(buf)-1] = '\0'; kde_tmp_dir += buf; } result = lstat(kde_tmp_dir.c_str(), &stat_buf); if ((result == 0) && (S_ISDIR(stat_buf.st_mode))) { /* $KDEHOME/socket-$HOSTNAME is a normal directory. Do nothing. */ tmp_buf = strdup(kde_tmp_dir.c_str()); return tmp_buf; } if ((result == -1) && (errno == ENOENT)) { // Link mising... return 0; } if ((result == -1) || (!S_ISLNK(stat_buf.st_mode))) { arts_warning("Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir.c_str()); return 0; } /* kde_tmp_dir is a link. Check whether it points to a valid directory. */ ssize_t size = 2048; tmp_buf = NULL; do { size *= 2; tmp_buf = (char *) realloc(tmp_buf, size); if (!tmp_buf) return 0; result = readlink(kde_tmp_dir.c_str(), tmp_buf, size - 1); if (result == -1) { arts_warning("Error: \"%s\" could not be read.\n", kde_tmp_dir.c_str()); free(tmp_buf); return 0; } } while(result == size - 1); tmp_buf[result] = '\0'; // printf("Link points to \"%s\"\n", tmp_buf); if (strncmp(tmp_buf, user_tmp_dir.c_str(), user_tmp_dir.length()) != 0) { arts_warning("Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir.c_str(), tmp_buf, user_tmp_dir.c_str()); free(tmp_buf); return 0; } result = check_tmp_dir(tmp_buf); if (result == 0) return tmp_buf; /* Success */ free(tmp_buf); return 0; } /* blatant and literal copy from lnusertemp to avoid kdelibs dependency */ /* Copyright (c) 2000 Waldo Bastian , released under LGPL */ static int create_link(const char *file, const char *tmp_dir) { int result; result = check_tmp_dir(tmp_dir); if (result) { return result; } result = symlink(tmp_dir, file); if (result == -1) { fprintf(stderr, "Error: Can not create link from \"%s\" to \"%s\"\n", file, tmp_dir); return 1; } printf("Created link from \"%s\" to \"%s\"\n", file, tmp_dir); return 0; } static int build_link(string tmp_prefix, const char *kde_prefix) { struct passwd *pw_ent; string kde_tmp_dir; string user_tmp_dir; char *tmp_buf; int uid = getuid(); const char *home_dir = getenv("HOME"); const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME"); int result; struct stat stat_buf; kde_tmp_dir = ""; pw_ent = getpwuid(uid); if (!pw_ent) { fprintf(stderr, "Error: Can not find password entry for uid %d.\n", getuid()); return 1; } user_tmp_dir = tmp_prefix + string(pw_ent->pw_name); if (!kde_home || !kde_home[0]) { kde_home = "~/.kde/"; } if (kde_home[0] == '~') { if (uid == 0) { home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root"; } if (!home_dir || !home_dir[0]) { fprintf(stderr, "Aborting. $HOME not set!"); exit(255); } kde_home++; kde_tmp_dir = string(home_dir); } kde_tmp_dir += kde_home; /** Strip trailing '/' **/ if ( kde_tmp_dir[kde_tmp_dir.length()-1] == '/') kde_tmp_dir.resize(kde_tmp_dir.length()-1); result = stat(kde_tmp_dir.c_str(), &stat_buf); if ((result == -1) && (errno == ENOENT)) { result = mkdir(kde_tmp_dir.c_str(), 0700); } if (result == -1) { return 1; } kde_tmp_dir += kde_prefix; { char buf[1024]; if (gethostname(buf, sizeof(buf)-1) != 0) { arts_fatal("Aborting. Could not determine hostname or hostname too long."); } buf[sizeof(buf)-1] = '\0'; kde_tmp_dir += buf; } result = lstat(kde_tmp_dir.c_str(), &stat_buf); if ((result == 0) && (S_ISDIR(stat_buf.st_mode))) { /* $KDEHOME/tmp is a normal directory. Do nothing. */ printf("Directory \"%s\" already exists.\n", kde_tmp_dir.c_str()); return 0; } if ((result == -1) && (errno == ENOENT)) { printf("Creating link %s.\n", kde_tmp_dir.c_str()); result = create_link(kde_tmp_dir.c_str(), user_tmp_dir.c_str()); if (result == 0) return 0; /* Success */ unlink(kde_tmp_dir.c_str()); user_tmp_dir += "XXXXXX"; tmp_buf = strdup(user_tmp_dir.c_str()); mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */ result = create_link(kde_tmp_dir.c_str(), tmp_buf); free(tmp_buf); return result; } if ((result == -1) || (!S_ISLNK(stat_buf.st_mode))) { fprintf(stderr, "Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir.c_str()); return 1; } /* kde_tmp_dir is a link. Check whether it points to a valid directory. */ ssize_t size = 2048; tmp_buf = NULL; do { size *= 2; tmp_buf = (char *) realloc(tmp_buf, size); if (!tmp_buf) return 0; result = readlink(kde_tmp_dir.c_str(), tmp_buf, size - 1); if (result == -1) { arts_warning("Error: \"%s\" could not be read.\n", kde_tmp_dir.c_str()); free(tmp_buf); return 0; } } while(result == size - 1); tmp_buf[result] = '\0'; printf("Link points to \"%s\"\n", tmp_buf); if (strncmp(tmp_buf, user_tmp_dir.c_str(), user_tmp_dir.length()) != 0) { fprintf(stderr, "Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir.c_str(), tmp_buf, user_tmp_dir.c_str()); free(tmp_buf); unlink(kde_tmp_dir.c_str()); printf("Creating link %s.\n", kde_tmp_dir.c_str()); result = create_link(kde_tmp_dir.c_str(), user_tmp_dir.c_str()); if (result == 0) return 0; /* Success */ unlink(kde_tmp_dir.c_str()); user_tmp_dir += "XXXXXX"; tmp_buf = strdup(user_tmp_dir.c_str()); mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */ result = create_link(kde_tmp_dir.c_str(), tmp_buf); free(tmp_buf); return result; } result = check_tmp_dir(tmp_buf); free(tmp_buf); if (result == 0) return 0; /* Success */ unlink(kde_tmp_dir.c_str()); user_tmp_dir += "XXXXXX"; tmp_buf = strdup(user_tmp_dir.c_str()); mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */ result = create_link(kde_tmp_dir.c_str(), tmp_buf); free(tmp_buf); return result; } string MCOPUtils::createFilePath(string name) { static char *mcop_dir = 0; if (!mcop_dir) { mcop_dir = locate_mcop_dir(); } if (!mcop_dir) { const char *tmp = 0; tmp = getenv("KDETMP"); if (!tmp || !tmp[0]) tmp = getenv("TMPDIR"); if (!tmp || !tmp[0]) tmp = "/tmp"; build_link(string(tmp) + "/ksocket-", "/socket-"); mcop_dir = locate_mcop_dir(); } if (!mcop_dir) { arts_fatal("can't create mcop directory"); } string tmpdir = mcop_dir; return tmpdir+"/"+name; } /* * try to figure out full hostname - this is important as every client which * tries to connect objects located here will need to be able to resolve that * correctly */ string MCOPUtils::getFullHostname() { char buffer[1024]; string result; struct hostent *hp; if(gethostname(buffer,1024) == 0) { buffer[1023] = '\0'; result = buffer; } else return "localhost"; /* * if gethostname() isn't a FQDN (guess that by checking whether it * contains a dot), try to look it up to ensure it is */ if(result.find('.') == string::npos && (hp = gethostbyname(buffer)) != 0) result = hp->h_name; return result; } string MCOPUtils::readConfigEntry(const string& key, const string& defaultValue) { const char *home = getenv("HOME"); if(home) { string rcname = home + string("/.mcoprc"); MCOPConfig config(rcname); return config.readEntry(key,defaultValue); } return defaultValue; } /** IID generation **/ unsigned long MCOPUtils::makeIID(const string& interfaceName) { static map *iidmapobj = 0; static unsigned long nextiid = 1; if(!iidmapobj) iidmapobj = new map; map& iidmap = *iidmapobj; if(iidmap.find(interfaceName) == iidmap.end()) { iidmap[interfaceName] = nextiid++; return nextiid-1; // no need to lookup in the map thrice } return iidmap[interfaceName]; } static vector *readPath(const string& name, const string& defaultValue) { vector *result = 0; const char *home = getenv("HOME"); if(home) { string rcname = home + string("/.mcoprc"); MCOPConfig config(rcname); result = config.readListEntry(name); } if(!result) result = new vector; if(result->empty()) result->push_back(defaultValue); return result; } const vector *MCOPUtils::extensionPath() { static vector *result = 0; if(!result) result = readPath("ExtensionPath", EXTENSION_DIR); return result; } const vector *MCOPUtils::traderPath() { static vector *result = 0; if(!result) { result = readPath("TraderPath", TRADER_DIR); const char *home = getenv("HOME"); if(home) result->push_back(home + string("/.mcop/trader-cache")); } return result; } string MCOPUtils::mcopDirectory() { static bool initialized = false; static string mcopDirectory; if(initialized) return mcopDirectory; initialized = true; const char *home = getenv("HOME"); arts_return_val_if_fail(home != 0, ""); mcopDirectory = home + string("/.mcop"); mkdir(home,0755); if(mkdir(mcopDirectory.c_str(),0755) != 0) { string why = strerror(errno); struct stat st; stat(mcopDirectory.c_str(),&st); if(!S_ISDIR(st.st_mode)) { arts_warning("can't create directory %s (%s)", mcopDirectory.c_str(), why.c_str()); mcopDirectory = ""; } } return mcopDirectory; } bool MCOPUtils::tokenize(const string& line, string& key,vector& values) { string value; enum { sKey, sValue, sValueQuoted, sValueQuotedEscaped, sBad } state; state = sKey; for(string::const_iterator i = line.begin(); i != line.end(); i++) { char c = *i; unsigned char uc = static_cast(c); arts_assert(c != '\n'); if(state == sKey) { if(c == ' ' || c == '\t') ; // ignore else if(isalnum(c)) key += c; else if(c == '=') state = sValue; else state = sBad; } else if(state == sValue) { if(c == ' ' || c == '\t') ; // ignore else if(c == '"') state = sValueQuoted; else if(c == ',') { values.push_back(value); value = ""; } else if(uc > 32 && uc < 128) value += c; else state = sBad; } else if(state == sValueQuoted) { if(c == '"') state = sValue; else if(c == '\\') state = sValueQuotedEscaped; else value += c; } else if(state == sValueQuotedEscaped) { value += c; state = sValueQuoted; } } if(state == sValue) values.push_back(value); return(state != sBad); }