1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 #include <config.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40 #include <sys/types.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <sys/stat.h>
44 #include <sys/param.h>
45 #include <pwd.h>
46 #include <errno.h>
47 #include <assert.h>
48 #include <fcntl.h>
49 
50 #include "uim.h"
51 #include "uim-internal.h"
52 #include "uim-scm.h"
53 #include "uim-scm-abbrev.h"
54 #include "uim-posix.h"
55 #include "uim-notify.h"
56 #include "gettext.h"
57 #include "dynlib.h"
58 
59 uim_bool
uim_get_user_name(char * name,int len,int uid)60 uim_get_user_name(char *name, int len, int uid)
61 {
62   struct passwd *pw;
63 
64   if (len <= 0)
65     return UIM_FALSE;
66   pw = getpwuid(uid);
67   if (!pw) {
68     name[0] = '\0';
69     return UIM_FALSE;
70   }
71   if (strlcpy(name, pw->pw_name, len) >= (size_t)len) {
72     name[0] = '\0';
73     endpwent();
74     return UIM_FALSE;
75   }
76   endpwent();
77   return UIM_TRUE;
78 }
79 
80 static uim_lisp
user_name(void)81 user_name(void)
82 {
83   char name[BUFSIZ];
84 
85   if (!uim_get_user_name(name, sizeof(name), getuid()))
86     return uim_scm_f();
87 
88   return MAKE_STR(name);
89 }
90 
91 uim_bool
uim_get_home_directory(char * home,int len,int uid)92 uim_get_home_directory(char *home, int len, int uid)
93 {
94   struct passwd *pw;
95 
96   if (len <= 0)
97     return UIM_FALSE;
98   pw = getpwuid(uid);
99   if (!pw) {
100     home[0] = '\0';
101     return UIM_FALSE;
102   }
103   if (strlcpy(home, pw->pw_dir, len) >= (size_t)len) {
104     home[0] = '\0';
105     endpwent();
106     return UIM_FALSE;
107   }
108   endpwent();
109   return UIM_TRUE;
110 }
111 
112 static uim_lisp
home_directory(uim_lisp user_)113 home_directory(uim_lisp user_)
114 {
115   int uid;
116   char home[MAXPATHLEN];
117 
118   if (INTP(user_)) {
119     uid = C_INT(user_);
120   } else if (STRP(user_)) {
121     struct passwd *pw;
122 
123     pw = getpwnam(REFER_C_STR(user_));
124 
125     if (!pw)
126       return uim_scm_f();
127 
128     uid = pw->pw_uid;
129     endpwent();
130   } else {
131     return uim_scm_f();
132   }
133 
134   if (!uim_get_home_directory(home, sizeof(home), uid)) {
135     char *home_env = getenv("HOME");
136     if (home_env)
137       return MAKE_STR(home_env);
138     return uim_scm_f();
139   }
140 
141   return MAKE_STR(home);
142 }
143 
144 static uim_bool
uim_check_dir_internal(const char * dir,int need_prepare)145 uim_check_dir_internal(const char *dir, int need_prepare)
146 {
147   struct stat st;
148 
149   if (stat(dir, &st) < 0)
150     if (need_prepare)
151       return (mkdir(dir, 0700) < 0) ? UIM_FALSE : UIM_TRUE;
152     else
153       return UIM_FALSE;
154   else {
155     mode_t mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
156     return ((st.st_mode & mode) == mode) ? UIM_TRUE : UIM_FALSE;
157   }
158 }
159 
160 /* FIXME: use appropriate name for this API */
161 uim_bool
uim_check_dir(const char * dir)162 uim_check_dir(const char *dir)
163 {
164   int need_prepare = UIM_TRUE;
165 
166   return uim_check_dir_internal(dir, need_prepare);
167 }
168 
169 static uim_lisp
c_prepare_dir(uim_lisp dir_)170 c_prepare_dir(uim_lisp dir_)
171 {
172   if (!uim_check_dir(REFER_C_STR(dir_))) {
173     return uim_scm_f();
174   }
175   return uim_scm_t();
176 }
177 
178 static uim_bool
uim_get_config_path_internal(char * path,int len,int is_getenv,int need_prepare)179 uim_get_config_path_internal(char *path, int len, int is_getenv, int need_prepare)
180 {
181   char home[MAXPATHLEN];
182 
183   if (len <= 0)
184     return UIM_FALSE;
185 
186   if (!uim_get_home_directory(home, sizeof(home), getuid()) && is_getenv) {
187     char *home_env = getenv("HOME");
188 
189     if (!home_env) {
190       path[0] = '\0';
191       return UIM_FALSE;
192     }
193 
194     if (strlcpy(home, home_env, sizeof(home)) >= sizeof(home)) {
195       path[0] = '\0';
196       return UIM_FALSE;
197     }
198   }
199 
200   if (snprintf(path, len, "%s/.uim.d", home) < 0) {
201     path[0] = '\0';
202     return UIM_FALSE;
203   }
204 
205   if (!uim_check_dir_internal(path, need_prepare)) {
206     return UIM_FALSE;
207   }
208 
209   return UIM_TRUE;
210 }
211 
212 /* FIXME: use appropriate name for this API */
213 uim_bool
uim_get_config_path(char * path,int len,int is_getenv)214 uim_get_config_path(char *path, int len, int is_getenv)
215 {
216   int need_prepare = UIM_TRUE;
217 
218   return uim_get_config_path_internal(path, len, is_getenv, need_prepare);
219 }
220 
221 static uim_lisp
c_prepare_config_path(uim_lisp is_getenv_)222 c_prepare_config_path(uim_lisp is_getenv_)
223 {
224   char path[MAXPATHLEN];
225   int need_prepare = UIM_TRUE;
226 
227   if (!uim_get_config_path_internal(path, sizeof(path), C_BOOL(is_getenv_), need_prepare))
228     return uim_scm_f();
229   return MAKE_STR(path);
230 }
231 
232 static uim_lisp
c_get_config_path(uim_lisp is_getenv_)233 c_get_config_path(uim_lisp is_getenv_)
234 {
235   char path[MAXPATHLEN];
236   int need_prepare = UIM_FALSE;
237 
238   /* No need to check the existence of path in this function */
239   uim_get_config_path_internal(path, sizeof(path), C_BOOL(is_getenv_), need_prepare);
240 
241   return MAKE_STR(path);
242 }
243 
244 static uim_lisp
file_stat_mode(uim_lisp filename,mode_t mode)245 file_stat_mode(uim_lisp filename, mode_t mode)
246 {
247   struct stat st;
248   int err;
249 
250   err = stat(REFER_C_STR(filename), &st);
251   if (err)
252     return uim_scm_f();  /* intentionally returns #f instead of error */
253 
254   return MAKE_BOOL((st.st_mode & mode) == mode);
255 }
256 
257 static uim_lisp
file_readablep(uim_lisp filename)258 file_readablep(uim_lisp filename)
259 {
260   return file_stat_mode(filename, S_IRUSR);
261 }
262 
263 static uim_lisp
file_writablep(uim_lisp filename)264 file_writablep(uim_lisp filename)
265 {
266   return file_stat_mode(filename, S_IWUSR);
267 }
268 
269 static uim_lisp
file_executablep(uim_lisp filename)270 file_executablep(uim_lisp filename)
271 {
272   return file_stat_mode(filename, S_IXUSR);
273 }
274 
275 static uim_lisp
file_regularp(uim_lisp filename)276 file_regularp(uim_lisp filename)
277 {
278   return file_stat_mode(filename, S_IFREG);
279 }
280 
281 static uim_lisp
file_directoryp(uim_lisp filename)282 file_directoryp(uim_lisp filename)
283 {
284   return file_stat_mode(filename, S_IFDIR);
285 }
286 
287 static uim_lisp
file_mtime(uim_lisp filename)288 file_mtime(uim_lisp filename)
289 {
290   struct stat st;
291   int err;
292 
293   err = stat(REFER_C_STR(filename), &st);
294   if (err)
295     ERROR_OBJ("stat failed for file", filename);
296 
297   return MAKE_INT(st.st_mtime);
298 }
299 
300 static uim_lisp
c_unlink(uim_lisp path_)301 c_unlink(uim_lisp path_)
302 {
303   return MAKE_INT(unlink(REFER_C_STR(path_)));
304 }
305 
306 static uim_lisp
c_mkdir(uim_lisp path_,uim_lisp mode_)307 c_mkdir(uim_lisp path_, uim_lisp mode_)
308 {
309   return MAKE_INT(mkdir(REFER_C_STR(path_), C_INT(mode_)));
310 }
311 
312 static uim_lisp
c_chdir(uim_lisp path_)313 c_chdir(uim_lisp path_)
314 {
315   return MAKE_INT(chdir(REFER_C_STR(path_)));
316 }
317 
318 static uim_lisp
c_getenv(uim_lisp str)319 c_getenv(uim_lisp str)
320 {
321   char *val;
322 
323   ENSURE_TYPE(str, str);
324 
325   val = getenv(REFER_C_STR(str));
326 
327   return (val) ? MAKE_STR(val) : uim_scm_f();
328 }
329 
330 static uim_lisp
c_setenv(uim_lisp name,uim_lisp val,uim_lisp overwrite)331 c_setenv(uim_lisp name, uim_lisp val, uim_lisp overwrite)
332 {
333   int err;
334 
335   err = setenv(REFER_C_STR(name), REFER_C_STR(val), TRUEP(overwrite));
336 
337   return MAKE_BOOL(!err);
338 }
339 
340 static uim_lisp
c_unsetenv(uim_lisp name)341 c_unsetenv(uim_lisp name)
342 {
343   unsetenv(REFER_C_STR(name));
344 
345   return uim_scm_t();
346 }
347 
348 static uim_lisp
c_getuid(void)349 c_getuid(void)
350 {
351   return MAKE_INT(getuid());
352 }
353 
354 static uim_lisp
c_getgid(void)355 c_getgid(void)
356 {
357   return MAKE_INT(getgid());
358 }
359 
360 static uim_lisp
setugidp(void)361 setugidp(void)
362 {
363   assert(uim_scm_gc_any_contextp());
364 
365   return MAKE_BOOL(uim_issetugid());
366 }
367 
368 static uim_lisp
c_setsid(void)369 c_setsid(void)
370 {
371   return MAKE_INT(setsid());
372 }
373 
374 static uim_lisp
time_t_to_uim_lisp(time_t t)375 time_t_to_uim_lisp(time_t t)
376 {
377   char t_str[64];
378 
379   snprintf(t_str, sizeof(t_str), "%.32g", (double)t);
380   return MAKE_STR(t_str);
381 }
382 
383 static uim_bool
uim_lisp_to_time_t(time_t * t,uim_lisp t_)384 uim_lisp_to_time_t(time_t *t, uim_lisp t_)
385 {
386   const char *t_str = REFER_C_STR(t_);
387   char *end;
388 
389   *t = (time_t)strtod(t_str, &end);
390   return *end == '\0';
391 }
392 
393 static uim_lisp
c_time(void)394 c_time(void)
395 {
396   time_t now;
397 
398   if ((time(&now)) == (time_t) -1)
399     return CONS(MAKE_SYM("error"), MAKE_STR(strerror(errno)));
400   return time_t_to_uim_lisp(now);
401 }
402 
403 static uim_lisp
c_difftime(uim_lisp time1_,uim_lisp time0_)404 c_difftime(uim_lisp time1_, uim_lisp time0_)
405 {
406   time_t time1, time0;
407 
408   if (!uim_lisp_to_time_t(&time1, time1_))
409     return uim_scm_f();
410   if (!uim_lisp_to_time_t(&time0, time0_))
411     return uim_scm_f();
412   return time_t_to_uim_lisp(difftime(time1, time0));
413 }
414 
415 
416 static uim_lisp
c_sleep(uim_lisp seconds_)417 c_sleep(uim_lisp seconds_)
418 {
419   return MAKE_INT(sleep((unsigned int)C_INT(seconds_)));
420 }
421 
422 static uim_lisp
c_error_string()423 c_error_string()
424 {
425   return MAKE_STR(strerror(errno));
426 }
427 
428 void
uim_init_posix_subrs(void)429 uim_init_posix_subrs(void)
430 {
431   uim_scm_init_proc0("user-name", user_name);
432   uim_scm_init_proc1("home-directory", home_directory);
433 
434   uim_scm_init_proc1("create/check-directory!", c_prepare_dir);
435   uim_scm_init_proc1("get-config-path!", c_prepare_config_path);
436   uim_scm_init_proc1("get-config-path", c_get_config_path);
437 
438   uim_scm_init_proc1("file-readable?", file_readablep);
439   uim_scm_init_proc1("file-writable?", file_writablep);
440   uim_scm_init_proc1("file-executable?", file_executablep);
441   uim_scm_init_proc1("file-regular?", file_regularp);
442   uim_scm_init_proc1("file-directory?", file_directoryp);
443   uim_scm_init_proc1("file-mtime", file_mtime);
444 
445   uim_scm_init_proc1("unlink", c_unlink);
446   uim_scm_init_proc2("mkdir", c_mkdir);
447   uim_scm_init_proc1("chdir", c_chdir);
448 
449   uim_scm_init_proc0("getuid", c_getuid);
450   uim_scm_init_proc0("getgid", c_getgid);
451   uim_scm_init_proc0("setugid?", setugidp);
452 
453   uim_scm_init_proc0("setsid", c_setsid);
454 
455   uim_scm_init_proc1("getenv", c_getenv);
456   uim_scm_init_proc3("setenv", c_setenv);
457   uim_scm_init_proc1("unsetenv", c_unsetenv);
458 
459   uim_scm_init_proc0("time", c_time);
460   uim_scm_init_proc2("difftime", c_difftime);
461 
462   uim_scm_init_proc1("sleep", c_sleep);
463 
464   uim_scm_init_proc0("posix-error-string", c_error_string);
465 }
466