2 * Name : application_configuration
3 * Author : Chris Koeritz
5 * Copyright (c) 1994-$now By Author. This program is free software; you can
6 * redistribute it and/or modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either version 2 of
8 * the License or (at your option) any later version. This is online at:
9 * http://www.fsf.org/copyleft/gpl.html
10 * Please send any updates to: fred@gruntose.com
13 #include "application_configuration.h"
14 #include "ini_configurator.h"
16 #include <application/windoze_helper.h>
17 #include <basis/environment.h>
18 #include <basis/functions.h>
19 #include <basis/guards.h>
20 #include <basis/mutex.h>
21 #include <basis/utf_conversion.h>
22 #include <filesystem/directory.h>
23 #include <filesystem/filename.h>
24 #include <mathematics/chaos.h>
25 #include <structures/static_memory_gremlin.h>
26 #include <textual/parser_bits.h>
29 #include <mach-o/dyld.h>
37 #include <sys/utsname.h>
45 using namespace basis;
46 using namespace filesystem;
47 using namespace mathematics;
48 using namespace structures;
49 using namespace textual;
52 #define LOG(to_print) printf("%s\n", astring(to_print).s())
54 namespace configuration {
56 const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE;
57 // maximum command line that we'll deal with here.
59 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
60 astring application_configuration::get_cmdline_from_proc()
62 FUNCDEF("get_cmdline_from_proc");
63 static astring __check_once_app_path;
64 //hmmm: we want to use a single per app static synch here!
65 if (__check_once_app_path.length()) return __check_once_app_path;
68 __check_once_app_path = query_for_process_info();
69 return __check_once_app_path;
72 // we have not looked this app's name up in the path yet.
73 a_sprintf cmds_filename("/proc/%d/cmdline", process_id());
74 FILE *cmds_file = fopen(cmds_filename.s(), "r");
76 LOG("failed to open our process's command line file.\n");
79 //hmmm: this would be a lot nicer using a byte filer.
81 char *filebuff = new char[size + 1];
82 ssize_t chars_read = getline((char **)&filebuff, &size, cmds_file);
83 // read the first line, giving ample space for how long it might be.
84 fclose(cmds_file); // drop the file again.
85 if (!chars_read || negative(chars_read)) {
86 LOG("failed to get any characters from our process's cmdline file.\n");
89 // copy the buffer into a string, which works great since the entries in the
90 // command line are all separated by zero characters.
91 __check_once_app_path = filebuff;
93 //printf("got an app name before chewing: %s\n", __check_once_app_path.s());
94 // clean out quote characters from the name.
95 for (int i = __check_once_app_path.length() - 1; i >= 0; i--) {
96 if (__check_once_app_path[i] == '"') __check_once_app_path.zap(i, i);
98 // check if the thing has a path attached to it. if it doesn't, we need to accentuate
99 // our knowledge about the file.
100 filename testing(__check_once_app_path);
101 if (testing.had_directory()) return __check_once_app_path; // all set.
103 //printf("no dir part found, app name after chewing: %s\n", __check_once_app_path.s());
105 //hmmm: the below might be better off as a find app in path method, which relies on which.
106 // there was no directory component, so we'll try to guess one.
107 astring temp_filename(environment::TMP()
108 + a_sprintf("/zz_cmdfind.%d", chaos().inclusive(0, 999999999)));
109 system((astring("which ") + __check_once_app_path + " >" + temp_filename).s());
110 FILE *which_file = fopen(temp_filename.s(), "r");
112 LOG("failed to open the temporary output from which.\n");
115 // reallocate the file buffer.
117 filebuff = new char[size + 1];
118 chars_read = getline((char **)&filebuff, &size, which_file);
120 unlink(temp_filename.s());
121 if (!chars_read || negative(chars_read)) {
122 LOG("failed to get any characters from the which cmd output.\n");
125 // we had some luck using 'which' to locate the file, so we'll use this version.
126 __check_once_app_path = filebuff;
127 while (parser_bits::is_eol(__check_once_app_path[__check_once_app_path.end()])) {
128 __check_once_app_path.zap(__check_once_app_path.end(), __check_once_app_path.end());
132 return __check_once_app_path; // return whatever which told us.
135 // deprecated; better to use the /proc/pid/cmdline file.
136 astring application_configuration::query_for_process_info()
138 FUNCDEF("query_for_process_info");
139 astring to_return = "unknown";
140 // we ask the operating system about our process identifier and store
141 // the results in a temporary file.
143 a_sprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", process_id(),
144 rando.inclusive(0, 128000));
146 a_sprintf cmd("ps -o args=\"\" %d >%s", process_id(),
149 a_sprintf cmd("ps h -O \"args\" %d >%s", process_id(),
152 // run the command to locate our process info.
153 int sysret = system(cmd.s());
154 if (negative(sysret)) {
155 LOG("failed to run ps command to get process info");
158 // open the output file for reading.
159 FILE *output = fopen(tmpfile.s(), "r");
161 LOG("failed to open the ps output file");
164 // read the file's contents into a string buffer.
165 char buff[MAXIMUM_COMMAND_LINE];
166 size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output);
168 // success at finding some text in the file at least.
169 while (size_read > 0) {
170 const char to_check = buff[size_read - 1];
171 if ( !to_check || (to_check == '\r') || (to_check == '\n')
172 || (to_check == '\t') )
176 to_return.reset(astring::UNTERMINATED, buff, size_read);
178 // couldn't read anything.
179 LOG("could not read output of process list");
186 // used as a return value when the name cannot be determined.
187 #define SET_BOGUS_NAME(error) { \
191 unlink(tmpfile.s()); \
193 astring home_dir = environment::get("HOME"); \
194 to_return = home_dir + "/failed_to_determine.exe"; \
197 astring application_configuration::application_name()
199 FUNCDEF("application_name");
202 char buffer[MAX_ABS_PATH] = { '\0' };
203 uint32_t buffsize = MAX_ABS_PATH - 1;
204 _NSGetExecutablePath(buffer, &buffsize);
205 to_return = (char *)buffer;
206 #elif defined(__UNIX__) || defined(__GNU_WINDOWS__)
207 to_return = get_cmdline_from_proc();
208 #elif defined(_MSC_VER)
209 flexichar low_buff[MAX_ABS_PATH + 1];
210 GetModuleFileName(NULL_POINTER, low_buff, MAX_ABS_PATH - 1);
211 astring buff = from_unicode_temp(low_buff);
212 buff.to_lower(); // we lower-case the name since windows seems to UC it.
215 #pragma error("hmmm: no means of finding app name is implemented.")
216 SET_BOGUS_NAME("not_implemented_for_this_OS");
221 #if defined(__UNIX__) || defined(_MSC_VER) || defined(__GNU_WINDOWS__)
222 basis::un_int application_configuration::process_id() { return getpid(); }
224 #pragma error("hmmm: need process id implementation for this OS!")
225 basis::un_int application_configuration::process_id() { return 0; }
228 astring application_configuration::current_directory()
232 char buff[MAX_ABS_PATH];
233 getcwd(buff, MAX_ABS_PATH - 1);
235 #elif defined(_MSC_VER)
236 flexichar low_buff[MAX_ABS_PATH + 1];
237 GetCurrentDirectory(MAX_ABS_PATH, low_buff);
238 to_return = from_unicode_temp(low_buff);
240 #pragma error("hmmm: need support for current directory on this OS.")
246 // implement the software product function.
247 const char *application_configuration::software_product_name()
249 #ifdef GLOBAL_PRODUCT_NAME
250 return GLOBAL_PRODUCT_NAME;
256 astring application_configuration::application_directory()
257 { return filename(application_name()).dirname().raw(); }
259 structures::version application_configuration::get_OS_version()
263 utsname kernel_parms;
264 uname(&kernel_parms);
265 to_return = version(kernel_parms.release);
266 #elif defined(_MSC_VER)
268 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
269 ::GetVersionEx(&info);
270 to_return = version(a_sprintf("%u.%u.%u.%u", basis::un_short(info.dwMajorVersion),
271 basis::un_short(info.dwMinorVersion), basis::un_short(info.dwPlatformId),
272 basis::un_short(info.dwBuildNumber)));
274 #pragma error("hmmm: need version info for this OS!")
281 const char *PATH_CONFIGURATION_FILENAME() { return "paths.ini"; }
283 astring application_configuration::application_configuration_file()
285 filename cfg_file(application_directory() + "/" + PATH_CONFIGURATION_FILENAME());
286 return cfg_file.raw();
289 const astring &application_configuration::GLOBAL_SECTION_NAME() { STATIC_STRING("Common"); }
291 const astring &application_configuration::LOGGING_FOLDER_NAME() { STATIC_STRING("LogPath"); }
293 const astring &application_configuration::WINDOZE_VIRTUAL_ROOT_NAME()
294 { STATIC_STRING("VirtualUnixRoot"); }
296 const astring &application_configuration::DEFAULT_VIRTUAL_UNIX_ROOT()
297 { STATIC_STRING("c:/cygwin"); }
301 astring application_configuration::get_virtual_unix_root()
304 // simple implementation for unix/linux; just tell the truth about the real root.
309 read the path out of the config file, which should have been set during the
310 build process if this is really windows.
312 astring virtual_root = read_item(WINDOZE_VIRTUAL_ROOT_NAME());
314 // if it has no length, we didn't get our setting! we'll limp along with a guess.
315 return DEFAULT_VIRTUAL_UNIX_ROOT;
323 ////const int MAX_LOG_PATH = 512;
324 // the maximum length of the entry stored for the log path.
326 astring application_configuration::get_logging_directory()
328 // new scheme is to just use the temporary directory, which can vary per user
329 // and which hopefully is always set to something usable.
330 astring def_log = environment::TMP();
331 // add logs directory underneath that.
333 // add the subdirectory for logs.
335 // now grab the current value for the name, if any.
336 astring log_dir = read_item(LOGGING_FOLDER_NAME());
337 // get the entry for the logging path.
339 // if the entry was absent, we set it.
340 //printf("did not find log dir in config file\n");
341 ini_configurator ini(application_configuration_file(),
342 ini_configurator::RETURN_ONLY,
343 ini_configurator::APPLICATION_DIRECTORY);
344 ini.store(GLOBAL_SECTION_NAME(), LOGGING_FOLDER_NAME(), def_log);
346 // they gave us something. let's replace the environment variables
347 // in their string so we resolve paths and such.
348 log_dir = parser_bits::substitute_env_vars(log_dir);
349 //printf("%s", (char *)a_sprintf("got log dir with %s value\n", log_dir.s()).s());
352 // now we make sure the directory exists.
353 filename testing(log_dir);
354 if (!testing.exists()) {
355 bool okay = directory::recursive_create(log_dir);
357 LOG(astring("failed to create logging directory: ") + log_dir);
358 // return a directory almost guaranteed to exist; best we can do in this case.
371 astring application_configuration::make_logfile_name(const astring &base_name)
372 { return get_logging_directory() + "/" + base_name; }
374 astring application_configuration::read_item(const astring &key_name)
376 filename ini_name = application_configuration_file();
377 ini_configurator ini(ini_name, ini_configurator::RETURN_ONLY,
378 ini_configurator::APPLICATION_DIRECTORY);
379 astring to_return = ini.load(GLOBAL_SECTION_NAME(), key_name, "");
381 // if the string has any length, then we process any environment
382 // variables found encoded in the value.
383 to_return = parser_bits::substitute_env_vars(to_return);