]> pd.if.org Git - zpackage/blob - zpm-add.c
expand docs
[zpackage] / zpm-add.c
1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <dirent.h>
9 #include <unistd.h>
10 #include <pwd.h>
11 #include <grp.h>
12 #include <errno.h>
13 #include <fcntl.h>
14
15 #include "zpm.h"
16 #include "sqlite/sqlite3.h"
17
18 struct opts {
19         char *package, *version;
20         int release;
21         int complete, addcontent, isconfig, opt_l, recursive, verbose;
22         int followsymlinks;
23         int xargs, noclear;
24         int striparg;
25         mode_t clmode;
26
27         char *cltype, *clhash, *clmtime, *prefix, *strip, *cltarget;
28         char *clgroup, *cluser;
29 };
30
31 #if 0
32 static void warn(char *fmt, ...) {
33         va_list args;
34
35         va_start(args, fmt);
36         vfprintf(stderr, fmt, args);
37         va_end(args);
38         fprintf(stderr, "\n");
39 }
40 #endif
41
42 static int dieval = EXIT_FAILURE;
43
44 static void die(char *fmt, ...) {
45         va_list args;
46
47         va_start(args, fmt);
48         vfprintf(stderr, fmt, args);
49         va_end(args);
50         fprintf(stderr, "\n");
51         exit(dieval);
52 }
53
54 char *cleanpath(char *path, char *strip, char *prefix) {
55         char *s = path;
56         char *t = s;
57         size_t len = 0;
58         char *new;
59
60         if (strip) {
61                 len = strlen(strip);
62                 if (strncmp(path, strip, len)) {
63                         /* strip is not a prefix */
64                         return 0;
65                 }
66                 s += len;
67         }
68
69         len += strlen(s)+1;
70
71         new = malloc(len);
72         if (!new) {
73                 return NULL;
74         }
75         memset(new, 0, len);
76
77         t = new;
78
79         if (*s == 0) {
80                 /* path is only stripped */
81                 return new;
82         }
83
84         if (prefix) {
85                 strcpy(new, prefix);
86                 t = new + strlen(prefix);
87         }
88
89         /* skip leading curdir */
90         if (*s && *s == '.' && s[1] && s[1] == '/') {
91                 s += 2;
92         }
93
94         /* skip leading slashes */
95         while (*s == '/') {
96                 s++;
97         }
98
99         if (*t != '/') {
100                 *t++ = '/';
101         }
102
103         for (; *s; s++) {
104                 /* skip multiple slashes */
105                 if (*s == '/' && s[1] && s[1] == '/') {
106                         continue;
107                 }
108                 /* skip trailing slash */
109                 if (*s == '/' && !s[1]) {
110                         continue;
111                 }
112
113                 /* skip curdirs */
114                 if (*s == '/' && s[1] && s[1] == '.' && s[2] && s[2] == '/') {
115                         s += 1;
116                         continue;
117                 }
118
119                 if (*s == '/' && s[1] && s[1] == '.' && s[2] == 0) {
120                         s += 1;
121                         continue;
122                 }
123
124                 *t++ = *s;
125         }
126         *t = 0;
127
128         return new;
129 }
130
131 char *getuser(uid_t uid) {
132         struct passwd *pw;
133
134         pw = getpwuid(uid);
135         if (pw) {
136                 return strdup(pw->pw_name);
137         }
138         return 0;
139 }
140
141 char *getgroup(uid_t uid) {
142         struct passwd *pw;
143
144         pw = getpwuid(uid);
145         if (pw) {
146                 return strdup(pw->pw_name);
147         }
148         return 0;
149 }
150
151 void free_file(struct zpm_file *file) {
152         free(file->path);
153         free(file->owner);
154         free(file->group);
155         free(file->target);
156         file->path = file->owner = file->group = file->target = 0;
157 }
158
159 int stat_file(struct zpm_file *file, char *path, struct opts *opt) {
160                 struct stat st;
161                 int rv;
162
163                 file->data = path;
164
165                 errno = 0;
166                 if (opt->followsymlinks) {
167                         rv = stat(path, &st);
168                 } else {
169                         rv = lstat(path, &st);
170                 }
171
172                 if (rv == -1) {
173                         return 0;
174                 }
175
176                 switch (st.st_mode & S_IFMT) {
177                         case S_IFBLK: file->type = 'b'; break;
178                         case S_IFCHR: file->type = 'c'; break;
179                         case S_IFDIR: file->type = 'd'; break;
180                         case S_IFIFO: file->type = 'p'; break;
181                         case S_IFLNK: file->type = 'l'; break;
182                         case S_IFREG: file->type = 'r'; break;
183                         case S_IFSOCK: file->type = 's'; break;
184                         default: file->type = 0; break;
185                 }
186
187                 file->configuration = opt->isconfig;
188
189                 if (file->type == 0) {
190                         return 0;
191                 }
192
193                 if (file->type == 'l') {
194                         if (opt->cltarget) {
195                                 file->target = strdup(opt->cltarget);
196                         } else {
197                                 char linkval[4096];
198                                 size_t n;
199                                 n = readlink(path, linkval, sizeof linkval);
200                                 if (n >= sizeof linkval) {
201                                         return 0;
202                                 }
203                                 linkval[n] = 0;
204                                 file->target = strdup(linkval);
205                         }
206                 }
207                 
208                 /* strip and prefix */
209                 file->path = cleanpath(path, opt->strip, opt->prefix);
210                 if (file->path == 0) {
211                         free_file(file);
212                         return 0;
213                 }
214
215                 if (opt->clmode) {
216                         file->mode = opt->clmode & 07777;
217                 } else {
218                         file->mode = st.st_mode & 07777;
219                 }
220
221                 if (opt->cluser) {
222                         file->owner = strdup(opt->cluser);
223                 } else {
224                         file->owner = getuser(st.st_uid);
225                 }
226
227                 if (file->owner == 0) {
228                         free_file(file);
229                         return 0;
230                 }
231
232                 if (opt->clgroup) {
233                         file->group = strdup(opt->clgroup);
234                 } else {
235                         file->group = getgroup(st.st_gid);
236                 }
237                 if (file->group == 0) {
238                         free_file(file);
239                         return 0;
240                 }
241
242                 file->mtime = st.st_mtime;
243
244                 return 1;
245 }
246
247 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt);
248
249 char *pathcat(char *dir, char *path) {
250         size_t dirlen = 0, pathlen = 0;
251         char *cat;
252
253         /* chop off trailing / on dir */
254         if (dir) {
255                 dirlen = strlen(dir);
256                 while (dirlen && dir[dirlen-1] == '/') {
257                         dirlen--;
258                 }
259         }
260
261         if (path) {
262                 pathlen = strlen(path);
263                 while (*path && *path == '/') {
264                         path++;
265                         pathlen--;
266                 }
267         }
268
269         cat = malloc(dirlen + pathlen + 2);
270         if (cat) {
271                 strncpy(cat, dir, dirlen);
272                 cat[dirlen] = '/';
273                 strcpy(cat+dirlen+1, path);
274         }
275         return cat;
276 }
277
278 int add_dir(struct zpm *zpm, char *path, struct opts *opt) {
279         DIR *dir;
280         struct dirent *de;
281         struct zpm_file file = { 0 };
282         int fail = 0;
283
284         char *dpath = 0;
285
286         dir = opendir(path);
287
288         if (!dir) {
289                 zpm_seterror(zpm, "can't open dir %s: %s", path, strerror(errno));
290                 return 0;
291         }
292
293         while ((de = readdir(dir))) {
294                 if (!strcmp(de->d_name, ".")) {
295                         continue;
296                 }
297                 if (!strcmp(de->d_name, "..")) {
298                         continue;
299                 }
300
301                 dpath = pathcat(path, de->d_name);
302                 if (!dpath) {
303                         zpm_seterror(zpm, "pathcat failed");
304                         fail = 1;
305                         break;
306                 }
307                 
308                 if (!stat_file(&file, dpath, opt)) {
309                         zpm_seterror(zpm, "stat %s failed: %s",
310                                         dpath, strerror(errno));
311                         fail = 1;
312                         break;
313                 }
314
315                 if (!add_file(zpm, &file, opt)) {
316                         free_file(&file);
317                         fail = 1;
318                         break;
319                 }
320                 free_file(&file);
321                 free(dpath);
322         }
323         closedir(dir);
324         return fail ? 0 : 1;
325 }
326
327 int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt) {
328         char hash[ZPM_HASH_STRLEN+1];
329
330         if (file->path[0] != 0) {
331                 if (file->type == 'r') {
332                         if (opt->addcontent) {
333                                 if (zpm_import(zpm, file->data, 0, hash)) {
334                                         strcpy(file->hash, hash);
335                                 } else {
336                                         return 0;
337                                 }
338                         } else {
339                                 zpm_hash(file->data, hash, 0);
340                                 strcpy(file->hash, hash);
341                         }
342                 }
343
344                 zpm_db_run(zpm, "insert or replace into packagefiles "
345                                 "(package,version,release,path,mode,mtime,username,"
346                                 "groupname,filetype,hash,configuration,target)"
347                                 " values "
348                                 "(%Q, %Q, %d, %Q, %o, %d, %Q, %Q, '%c', %Q, %d, %Q)",
349                                 opt->package, opt->version, opt->release, file->path,
350                                 file->mode, (int)file->mtime, file->owner, file->group,
351                                 file->type,
352                                 file->type == 'r' ? file->hash : NULL,
353                                 file->configuration,
354                                 file->type == 'l' ? file->target : NULL
355                           );
356
357                 if (zpm->error) {
358                         return 0;
359                 }
360
361                 if (opt->verbose > 1) {
362                         printf("%c%o %s:%s %s\n", file->type, file->mode, file->owner, file->group, file->path);
363                 } else if (opt->verbose > 0) {
364
365                         printf("%s\n", file->path);
366                         fflush(stdout);
367                 }
368         }
369
370         if (opt->recursive && file->type == 'd') {
371                 if (!add_dir(zpm, file->data, opt)) {
372                         return 0;
373                 }
374         }
375
376         return 1;
377 }
378
379 int main(int ac, char **av) {
380         struct zpm zpm;
381         struct opts opt = { 0 };
382         int i;
383         int option;
384
385         char hash[ZPM_HASH_STRLEN+1];
386
387         char *dbfile = getenv("ZPMDB");
388
389         if (!dbfile) {
390                 dbfile = "/var/lib/zpm/local.db";
391         }
392
393         opt.addcontent = 1;
394
395         while ((option = getopt(ac, av, "CF:H:M:NP:S:T:cf:g:hlm:ru:vxz")) != -1) {
396                 switch (option) {
397                         case 'C': opt.complete = 1; break;
398                         case 'F': opt.cltype = optarg; break;
399                         case 'H': opt.clhash = optarg; break;
400                         case 'M': opt.clmtime = optarg; break;
401                         case 'N': opt.addcontent = 0; break;
402                         case 'P': opt.prefix = optarg; break;
403                         case 'S': opt.strip = optarg; break;
404                         case 's': opt.striparg = 1; break;
405                         case 'T': opt.cltarget = optarg; break;
406                         case 'c': opt.isconfig = 1; break;
407                         case 'f': dbfile = optarg; break;
408                         case 'g': opt.clgroup = optarg; break;
409                         case 'h': opt.followsymlinks = 1; break;
410                         case 'l': opt.opt_l = 1; break;
411                         case 'm': opt.clmode = strtol(optarg, NULL, 8); break;
412                         case 'r': opt.recursive = 1; break;
413                         case 'u': opt.cluser = optarg; break;
414                         case 'v': opt.verbose++; break;
415                         case 'x': opt.xargs = 1; dieval = 255; break;
416                         case 'z': opt.noclear = 1; break;
417                         default:
418                                   exit(EXIT_FAILURE);
419                                   break;
420                 }
421         }
422
423         if (ac < optind) {
424                 exit(EXIT_FAILURE);
425         }
426
427         if (!zpm_open(&zpm, dbfile)) {
428                 die("can't open zpm db %s", dbfile);
429         }
430
431 #if 0
432         i = sqlite3_config(SQLITE_CONFIG_MMAP_SIZE,98222080);
433         if (i != SQLITE_OK) {
434                 exit(3);
435         }
436 #endif
437
438         /* package is first arg */
439         char *pkgstr;
440         char *pkgid;
441         pkgstr = av[optind++];
442         pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
443         if (!pkgid) {
444                 die("package %s not found\n", pkgstr);
445         }
446
447         char package[128];
448         char version[64];
449         int release;
450
451         zpm_parse_package(pkgid, package, version, &release);
452         opt.package = package;
453         opt.version = version;
454         opt.release = release;
455
456         if (opt.verbose) {
457                 printf("adding to %s %s\n", dbfile, pkgid);
458         }
459         fflush(stdout);
460
461         //zpm_begin(&zpm);
462
463         struct zpm_file file;
464         for (i=optind; av[i]; i++) {
465                 if (!stat_file(&file, av[i], &opt)) {
466                         die("can't stat %s: %s", av[i], strerror(errno));
467                 }
468                 file.data = av[i];
469                 if (opt.striparg) {
470                         opt.strip = av[i];
471                 }
472
473                 if (add_file(&zpm, &file, &opt)) {
474                         free_file(&file);
475                 } else {
476                         die("error: %s", zpm.errmsg);
477                 }
478         }
479
480         if (opt.complete) {
481                 zpm_db_run(&zpm, "update packages set build_time = %d where package = %Q and version = %Q and release = %d", time(NULL), package, version, release);
482                 zpm_package_hash(&zpm, pkgid, hash);
483                 zpm_package_sethash(&zpm, pkgid, hash);
484         } else if (!opt.noclear) {
485                 zpm_db_run(&zpm, "update packages set build_time = null, hash = null where package = %Q and version = %Q and release = %d", package, version, release);
486         }
487         /* TODO error check */
488
489         //zpm_commit(&zpm);
490         zpm_close(&zpm);
491         return 0;
492 }