1/*
2 This file is part of the KDE libraries
3 Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "config.h"
21#include <config-kstandarddirs.h>
22
23#include <sys/types.h>
24#include <sys/param.h>
25#include <sys/socket.h>
26#include <sys/stat.h>
27#include <sys/un.h>
28
29#include <errno.h>
30#include <string.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <pwd.h>
35#ifdef HAVE_LIMITS_H
36#include <limits.h>
37#endif
38
39int check_tmp_dir(const char *tmp_dir, int check_ownership);
40int create_link(const char *file, const char *tmp_dir);
41int build_link(const char* tmp, const char *tmp_prefix, const char *kde_prefix);
42
43/* checks that tmp_dir exists, otherwise creates it
44 * @param check_ownership: if 1, also check that user owns the dir
45 * returns 0 on success
46 */
47int check_tmp_dir(const char *tmp_dir, int check_ownership)
48{
49 /* reserve some space for an error string + a path name */
50 char errorstring[PATH_MAX+1024];
51 int result;
52 struct stat stat_buf;
53 result = lstat(tmp_dir, &stat_buf);
54 if ((result == -1) && (errno == ENOENT))
55 {
56#ifdef _WIN32
57 result = mkdir(tmp_dir);
58#else
59 result = mkdir(tmp_dir, 0700);
60#endif
61 if (result == -1)
62 {
63 snprintf(errorstring, sizeof(errorstring), "Error: cannot create directory \"%s\"", tmp_dir);
64 perror(errorstring);
65 return 1;
66 }
67 result = stat(tmp_dir, &stat_buf);
68 }
69 if ((result == -1) || ( !S_ISDIR(stat_buf.st_mode) && !S_ISLNK(stat_buf.st_mode) ))
70 {
71 fprintf(stderr, "Error: \"%s\" is not a directory.\n", tmp_dir);
72 return 1;
73 }
74
75 if (check_ownership && stat_buf.st_uid != getuid())
76 {
77 fprintf(stderr, "Error: \"%s\" is owned by uid %d instead of uid %d.\n", tmp_dir, stat_buf.st_uid, getuid());
78 return 1;
79 }
80 return 0;
81}
82
83int create_link(const char *file, const char *tmp_dir)
84{
85 int result;
86 result = check_tmp_dir(tmp_dir, 1);
87 if (result)
88 {
89 return result;
90 }
91 result = symlink(tmp_dir, file);
92 if (result == -1)
93 {
94 fprintf(stderr, "Error: Can not create link from \"%s\" to \"%s\"\n", file, tmp_dir);
95 return 1;
96 }
97#ifndef NDEBUG
98 fprintf(stderr,"Created link from \"%s\" to \"%s\"\n", file, tmp_dir);
99#endif
100 return 0;
101}
102
103
104int build_link(const char* tmp, const char *tmp_prefix, const char *kde_prefix)
105{
106 struct passwd *pw_ent;
107 char kde_tmp_dir[PATH_MAX+1];
108 char user_tmp_dir[PATH_MAX+1];
109 char tmp_buf[PATH_MAX+1];
110 int uid = getuid();
111 const char *home_dir = getenv("HOME");
112 const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME");
113 int result;
114 struct stat stat_buf;
115
116 kde_tmp_dir[0] = 0;
117
118 pw_ent = getpwuid(uid);
119 if (!pw_ent)
120 {
121 fprintf(stderr, "Error: Can not find password entry for uid %d.\n", getuid());
122 return 1;
123 }
124
125 strncpy(user_tmp_dir, tmp_prefix, PATH_MAX);
126 user_tmp_dir[ PATH_MAX ] = '\0';
127 strncat(user_tmp_dir, pw_ent->pw_name, PATH_MAX - strlen(tmp_prefix));
128
129 if (!kde_home || !kde_home[0])
130 {
131 kde_home = "~/" KDE_DEFAULT_HOME "/";
132 }
133
134 if (kde_home[0] == '~')
135 {
136 if (uid == 0)
137 {
138 home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root";
139 }
140 if (!home_dir || !home_dir[0])
141 {
142 fprintf(stderr, "Aborting. $HOME not set!\n");
143 return 1;
144 }
145 if (strlen(home_dir) > (PATH_MAX-100))
146 {
147 fprintf(stderr, "Aborting. Home directory path too long!\n");
148 return 1;
149 }
150 kde_home++;
151 strncpy(kde_tmp_dir, home_dir, PATH_MAX);
152 kde_tmp_dir[ PATH_MAX ] = '\0';
153 }
154 strncat(kde_tmp_dir, kde_home, PATH_MAX - strlen(kde_tmp_dir));
155
156 /** Strip trailing '/' **/
157 if ( kde_tmp_dir[strlen(kde_tmp_dir)-1] == '/')
158 kde_tmp_dir[strlen(kde_tmp_dir)-1] = 0;
159
160 result = stat(kde_tmp_dir, &stat_buf);
161 if ((result == -1) && (errno == ENOENT))
162 {
163#ifdef _WIN32
164 result = mkdir(kde_tmp_dir);
165#else
166 result = mkdir(kde_tmp_dir, 0700);
167#endif
168 }
169 if (result == -1)
170 {
171 perror("mkdir failed: ");
172 return 1;
173 }
174
175 strncat(kde_tmp_dir, kde_prefix, PATH_MAX - strlen(kde_tmp_dir));
176 if (gethostname(kde_tmp_dir+strlen(kde_tmp_dir), PATH_MAX - strlen(kde_tmp_dir) - 1) != 0)
177 {
178 perror("Aborting. Could not determine hostname: ");
179 exit(255);
180 }
181 kde_tmp_dir[sizeof(kde_tmp_dir)-1] = '\0';
182
183 result = lstat(kde_tmp_dir, &stat_buf);
184 if ((result == 0) && (S_ISDIR(stat_buf.st_mode)))
185 {
186 /* $KDEHOME/tmp is a normal directory. Do nothing. */
187#ifndef NDEBUG
188 fprintf(stderr,"Directory \"%s\" already exists.\n", kde_tmp_dir);
189#endif
190 return 0;
191 }
192 if ((result == -1) && (errno == ENOENT))
193 {
194#ifndef NDEBUG
195 fprintf(stderr,"Creating link %s.\n", kde_tmp_dir);
196#endif
197 result = create_link(kde_tmp_dir, user_tmp_dir);
198 if (result == 0) return 0; /* Success */
199 unlink(kde_tmp_dir);
200 strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
201#if 0
202 mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
203#else
204 if (mkdtemp(user_tmp_dir)==0) return 1; /*JOWENN: isn't that the better solution ?? */
205#endif
206 return create_link(kde_tmp_dir, user_tmp_dir);
207 }
208 if ((result == -1) || (!S_ISLNK(stat_buf.st_mode)))
209 {
210 fprintf(stderr, "Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir);
211 return 1;
212 }
213 /* kde_tmp_dir is a link. Check whether it points to a valid directory. */
214 result = readlink(kde_tmp_dir, tmp_buf, PATH_MAX);
215 if (result == -1)
216 {
217 fprintf(stderr, "Error: \"%s\" could not be read.\n", kde_tmp_dir);
218 return 1;
219 }
220 tmp_buf[result] = '\0';
221#ifndef NDEBUG
222 fprintf(stderr,"Link points to \"%s\"\n", tmp_buf);
223#endif
224 if (strncmp(tmp_buf, user_tmp_dir, strlen(user_tmp_dir)) != 0)
225 {
226 fprintf(stderr, "Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir, tmp_buf, user_tmp_dir);
227 unlink(kde_tmp_dir);
228#ifndef NDEBUG
229 fprintf(stderr, "Creating link %s.\n", kde_tmp_dir);
230#endif
231 result = create_link(kde_tmp_dir, user_tmp_dir);
232 if (result == 0) return 0; /* Success */
233 unlink(kde_tmp_dir);
234 strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
235#if 0
236 mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
237#else
238 if (mkdtemp(user_tmp_dir)==0) return 1; /*JOWENN: isn't that the better solution ?? */
239#endif
240 return create_link(kde_tmp_dir, user_tmp_dir);
241 }
242 result = check_tmp_dir(tmp, 0);
243 if (result != 0) return result; /* Failure to create parent dir */
244 result = check_tmp_dir(tmp_buf, 1);
245 if (result == 0) return 0; /* Success */
246 unlink(kde_tmp_dir);
247 strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
248#if 0
249 mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
250#else
251 if (mkdtemp(user_tmp_dir)==0) return 1; /*JOWENN: isn't that the better solution ?? */
252#endif
253 return create_link(kde_tmp_dir, user_tmp_dir);
254}
255
256int main(int argc, char **argv)
257{
258 const char *tmp = 0;
259 char *tmp_prefix = 0;
260 const char *kde_prefix = 0;
261 int res = 0;
262
263 if ((argc != 2) ||
264 ((strcmp(argv[1], "tmp")!=0) &&
265 (strcmp(argv[1], "socket")!=0) &&
266 (strcmp(argv[1], "cache")!=0)))
267 {
268 fprintf(stderr, "Usage: lnusertemp tmp|socket|cache\n");
269 return 1;
270 }
271
272 tmp = getenv("KDETMP");
273 if (!tmp || !tmp[0])
274 tmp = getenv("TMPDIR");
275 if (!tmp || !tmp[0])
276 tmp = "/tmp";
277
278 if (strcmp(argv[1], "tmp") == 0)
279 {
280 tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/kde-")+1);
281 strcpy(tmp_prefix, tmp);
282 strcat(tmp_prefix, "/kde-");
283
284 kde_prefix = "/tmp-";
285 }
286 else if (strcmp(argv[1], "socket") == 0)
287 {
288 tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/ksocket-")+1);
289 strcpy(tmp_prefix, tmp );
290 strcat(tmp_prefix, "/ksocket-" );
291
292 kde_prefix = "/socket-";
293 }
294 else if (strcmp(argv[1], "cache") == 0)
295 {
296 tmp = getenv("KDEVARTMP");
297 if (!tmp || !tmp[0])
298 tmp = "/var/tmp";
299
300 tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/kdecache-")+1);
301 strcpy(tmp_prefix, tmp );
302 strcat(tmp_prefix, "/kdecache-" );
303
304 kde_prefix = "/cache-";
305 }
306
307 res = build_link(tmp, tmp_prefix, kde_prefix);
308
309 free(tmp_prefix);
310
311 return res;
312}
313