1 /* import.c:
2  *
3  ****************************************************************
4  * Copyright (C) 2003 Tom Lord
5  *
6  * See the file "COPYING" for further information about
7  * the copyright and warranty status of this work.
8  */
9 
10 
11 #include "hackerlab/bugs/panic.h"
12 #include "hackerlab/os/time.h"
13 #include "hackerlab/char/str.h"
14 #include "hackerlab/fs/file-names.h"
15 #include "hackerlab/vu/safe.h"
16 #include "tla/libdate/date-string.h"
17 #include "tla/libfsutils/rmrf.h"
18 #include "tla/libfsutils/string-files.h"
19 #include "tla/libfsutils/copy-file.h"
20 #include "tla/libfsutils/tmp-files.h"
21 #include "tla/libfsutils/rmrf.h"
22 #include "tla/libarch/patch-logs.h"
23 #include "tla/libarch/invent.h"
24 #include "tla/libarch/my.h"
25 #include "tla/libarch/hooks.h"
26 #include "tla/libarch/namespace.h"
27 #include "tla/libarch/pristines.h"
28 #include "tla/libarch/project-tree.h"
29 #include "tla/libarch/changelogs.h"
30 #include "tla/libarch/local-cache.h"
31 #include "tla/libarch/import.h"
32 
33 
34 /* __STDC__ prototypes for static functions */
35 
36 
37 
38 static t_uchar * arch_prepare_import_pristine (t_uchar ** changelog_loc_ret,
39                                                t_uchar ** cooked_log_ret,
40                                                t_uchar * tree_root,
41                                                t_uchar * raw_log,
42                                                t_uchar * archive,
43                                                t_uchar * version,
44                                                int full_meta);
45 static void arch_import_mid_commit (t_uchar * tree_root, t_uchar * cooked_log);
46 static void arch_finish_import (t_uchar * tree_root,
47                                 t_uchar * archive,
48                                 t_uchar * version,
49                                 t_uchar * pristine,
50                                 t_uchar * changelog_loc,
51                                 int full_meta);
52 static void arch_import_failed (t_uchar * tree_root, t_uchar * archive, t_uchar * version, t_uchar * pristine);
53 static rel_table all_patches (t_uchar * tree_root);
54 
55 
56 
57 void
arch_import(struct arch_archive * arch,t_uchar * version,t_uchar * tree_root,t_uchar * raw_log)58 arch_import (struct arch_archive * arch,
59              t_uchar * version,
60              t_uchar * tree_root,
61              t_uchar * raw_log)
62 {
63   t_uchar * changelog_loc = 0;
64   t_uchar * errstr;
65   t_uchar * pristine_path = 0;
66   t_uchar * cooked_log = 0;
67   t_uchar * my_uid = 0;
68   t_uchar * txn_id = 0;
69   t_uchar * revision;
70   const int full_meta = arch_tree_has_meta_flag (tree_root);
71 
72   revision = str_alloc_cat (0, version, "--base-0");
73 
74   pristine_path = arch_prepare_import_pristine (&changelog_loc, &cooked_log, tree_root, raw_log, arch->name, version, full_meta);
75 
76   my_uid = arch_my_id_uid ();
77   txn_id = arch_generate_txn_id ();
78   if (arch_revision_exists (arch, revision))
79     {
80       safe_printfmt (2, "arch_import: the revision already exists\n    revision: %s/%s\n", arch->official_name, revision);
81       exit (2);
82     }
83 
84   if (arch_archive_lock_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
85     {
86       safe_printfmt (2, "arch_import: unable to acquire revision lock (%s)\n    tree: %s\n    revision: %s/%s--base-0\n",
87                      errstr, tree_root, arch->name, version);
88       exit (2);
89     }
90 
91   if (arch_archive_put_log (&errstr, arch, version, 0, my_uid, txn_id, cooked_log))
92     {
93       safe_printfmt (2, "arch_import: unable to send log message to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
94                      errstr, tree_root, arch->name, version);
95       exit (2);
96     }
97 
98   if (arch_archive_put_import (&errstr, arch, version, 0, my_uid, txn_id, "base-0", pristine_path))
99     {
100       safe_printfmt (2, "arch_import: unable to send import tree to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
101                      errstr, tree_root, arch->name, version);
102       exit (2);
103     }
104 
105   if (arch_revision_ready (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
106     {
107       safe_printfmt (2, "arch_import: error sending tree to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
108                      errstr, tree_root, arch->name, version);
109       exit (2);
110     }
111 
112   arch_import_mid_commit (tree_root, cooked_log);
113 
114   if (arch_archive_finish_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
115     {
116       arch_import_failed (tree_root, arch->name, version, pristine_path);
117       safe_printfmt (2, "arch_import: unable to complete import transaction (%s)\n    tree: %s\n    revision: %s/%s\n",
118                      errstr, tree_root, arch->name, version);
119       exit (2);
120     }
121 
122   arch_finish_import (tree_root, arch->name, version, pristine_path, changelog_loc, full_meta);
123 
124   arch_run_hook ("import", "ARCH_ARCHIVE", arch->name, "ARCH_REVISION", revision, "ARCH_TREE_ROOT", tree_root, (t_uchar*)0);
125 
126   lim_free (0, revision);
127   lim_free (0, changelog_loc);
128   lim_free (0, pristine_path);
129   lim_free (0, cooked_log);
130   lim_free (0, my_uid);
131   lim_free (0, txn_id);
132 }
133 
134 
135 
136 
137 static t_uchar *
arch_prepare_import_pristine(t_uchar ** changelog_loc_ret,t_uchar ** cooked_log_ret,t_uchar * tree_root,t_uchar * raw_log,t_uchar * archive,t_uchar * version,int full_meta)138 arch_prepare_import_pristine (t_uchar ** changelog_loc_ret,
139                               t_uchar ** cooked_log_ret,
140                               t_uchar * tree_root,
141                               t_uchar * raw_log,
142                               t_uchar * archive,
143                               t_uchar * version,
144                               int full_meta)
145 {
146   t_uchar * tmp_stem = 0;
147   t_uchar * tmp_path = 0;
148   t_uchar * revision = 0;
149   t_uchar * pristine_path = 0;
150   rel_table inventory = rel_table_nil;
151   rel_table new_files_list = rel_table_nil;
152   t_uchar * log_message = 0;
153   t_uchar * pristine_log_path = 0;
154   int pristine_log_fd;
155 
156   /****************************************************************
157    * double check that we were handed a valid log message, if any
158    */
159   if (raw_log)
160     invariant (arch_valid_log_file (raw_log));
161 
162 
163   /****************************************************************
164    * make a temp dir for the pristine copy of the import
165    */
166   tmp_stem = str_alloc_cat_many (0, ",,import.", version, "--base-0--", archive, str_end);
167   tmp_path = tmp_file_name (tree_root, tmp_stem);
168 
169   rmrf_file (tmp_path);
170   safe_mkdir (tmp_path, 0777);
171 
172   revision = str_alloc_cat (0, version, "--base-0");
173   pristine_path = file_name_in_vicinity (0, tmp_path, revision);
174 
175   inventory = arch_source_inventory (tree_root, 1, 0, 0);
176 
177   safe_mkdir (pristine_path, 0777);
178 
179   copy_file_list (pristine_path, tree_root, inventory, full_meta);
180 
181 
182   /****************************************************************
183    * As long as the cache is hot with directories and inodes, compute
184    * a list of files for the New-files: header in the log message.
185    */
186   new_files_list = arch_source_files_inventory (tree_root, 0, 0);
187 
188 
189   /****************************************************************
190    * Create a log message for the newly committed base-0.
191    *
192    * Although the message is empty at this point, it's important for
193    * it to appear in directory listings of the patch-log, so that it
194    * shows up in the New-patches: header.
195    */
196   pristine_log_path = arch_log_file (pristine_path, archive, revision);
197   pristine_log_fd = safe_open (pristine_log_path, O_WRONLY | O_CREAT | O_EXCL, 0666);
198 
199 
200   /****************************************************************
201    * Generate the actual log message, adding automatically generated
202    * headers either to the supplied raw-log or to a default raw-log.
203    */
204   {
205     t_uchar * my_id = 0;
206     time_t now;
207     t_uchar * std_date = 0;
208     t_uchar * human_date = 0;
209     int log_fd;
210     rel_table patches = rel_table_nil;
211     t_uchar * eoh;
212 
213     my_id = arch_my_id ();
214     now = time(0);
215     std_date = standard_date (now);
216     human_date = pretty_date (now);
217 
218     log_fd = make_output_to_string_fd ();
219 
220     safe_printfmt (log_fd, "Revision: %s--base-0\n", version);
221     safe_printfmt (log_fd, "Archive: %s\n", archive);
222     safe_printfmt (log_fd, "Creator: %s\n", my_id);
223     safe_printfmt (log_fd, "Date: %s\n", human_date);
224     safe_printfmt (log_fd, "Standard-date: %s\n", std_date);
225 
226     /********************************
227      * Copy headers from the raw log
228      */
229     if (raw_log)
230       {
231         eoh = raw_log;
232         while (1)
233           {
234             eoh = str_chr_index (eoh, '\n');
235             if (!eoh || (eoh[1] == '\n') || (!eoh[1]))
236               break;
237             ++eoh;
238           }
239 
240         if (eoh)
241           {
242             eoh = eoh + 1;
243             safe_printfmt (log_fd, "%.*s", (int)(eoh - raw_log), raw_log);
244           }
245       }
246     else
247       {
248         safe_printfmt (log_fd, "Summary: initial import\n");
249       }
250 
251     /********************************
252      * automatic headers for New-files: and New-patches:
253      */
254     arch_print_log_list_header (log_fd, "New-files", new_files_list, 0);
255     patches = all_patches (pristine_path);
256     arch_print_log_list_header (log_fd, "New-patches", patches, 0);
257 
258     /********************************
259      * copy or generate the log body
260      */
261     if (!raw_log)
262       safe_printfmt (log_fd, "\n\n(automatically generated log message)\n");
263     else
264       {
265         if (*eoh)
266           {
267             safe_printfmt (log_fd, "%s", eoh);
268           }
269         else
270           {
271             safe_printfmt (log_fd, "\n\n");
272           }
273       }
274 
275     /********************************
276      * oh... did i mention we were writing
277      * the log to a string?
278      */
279     log_message = string_fd_close (log_fd);
280 
281     lim_free (0, my_id);
282     lim_free (0, std_date);
283     lim_free (0, human_date);
284     rel_free_table (patches);
285   }
286 
287   /****************************************************************
288    * Write the log into the patch-log of the pristine tree.
289    */
290   safe_printfmt (pristine_log_fd, "%s", log_message);
291   safe_close (pristine_log_fd);
292 
293   /****************************************************************
294    * If the import tree has a ChangeLog for this version,
295    * update it in the pristine tree.
296    */
297   {
298     int x;
299     t_uchar * changelog_id_suffix = 0;
300     t_uchar * changelog_x_id = 0;
301     t_uchar * changelog_i_id = 0;
302 
303     changelog_id_suffix = str_alloc_cat_many (0, "_automatic-ChangeLog--", archive, "/", version, str_end);
304     changelog_x_id = str_alloc_cat (0, "x", changelog_id_suffix);
305     changelog_i_id = str_alloc_cat (0, "i", changelog_id_suffix);
306 
307     for (x = 0; x < rel_n_records (inventory); ++x)
308       {
309         if (!str_cmp (changelog_x_id, rel_peek_str (inventory, x, 1)) || !str_cmp (changelog_i_id, rel_peek_str (inventory, x, 1)))
310           {
311             struct stat clstatb;
312             t_uchar * changelog_path = 0;
313             mode_t clmode;
314             int out_fd = -1;
315 
316             if (changelog_loc_ret)
317               *changelog_loc_ret = str_save (0, rel_peek_str (inventory, x, 0));
318 
319             changelog_path = file_name_in_vicinity (0, pristine_path, rel_peek_str (inventory, x, 0));
320 
321             safe_stat (changelog_path, &clstatb);
322             if (full_meta)
323               {
324                 clmode = clstatb.st_mode & 07777;
325               }
326             else
327               {
328                 clmode = clstatb.st_mode & 0777;
329               }
330             out_fd = safe_open (changelog_path, O_WRONLY | O_CREAT | O_TRUNC, clmode);
331             safe_fchmod (out_fd, clmode);
332             if (full_meta)
333               safe_fchown (out_fd, clstatb.st_uid, clstatb.st_gid);
334             arch_generate_changelog (out_fd, pristine_path, 0, 0, 0, 0, archive, version);
335             safe_close (out_fd);
336 
337             lim_free (0, changelog_path);
338 
339             break;
340           }
341       }
342 
343     lim_free (0, changelog_id_suffix);
344     lim_free (0, changelog_x_id);
345     lim_free (0, changelog_i_id);
346   }
347 
348 
349   /****************************************************************
350    * Give a copy of the log to the caller.
351    */
352   if (cooked_log_ret)
353     *cooked_log_ret = str_save (0, log_message);
354 
355 
356   lim_free (0, tmp_stem);
357   lim_free (0, tmp_path);
358   lim_free (0, revision);
359   rel_free_table (inventory);
360   rel_free_table (new_files_list);
361   lim_free (0, log_message);
362   lim_free (0, pristine_log_path);
363 
364   /****************************************************************
365    * Give the user the path to the pristine tree for base-0.
366    * It's up tot he caller to stash this in the archive.
367    */
368   return pristine_path;
369 }
370 
371 
372 static void
arch_import_mid_commit(t_uchar * tree_root,t_uchar * cooked_log)373 arch_import_mid_commit (t_uchar * tree_root, t_uchar * cooked_log)
374 {
375   arch_start_tree_commit (tree_root, cooked_log);
376 }
377 
378 
379 static void
arch_finish_import(t_uchar * tree_root,t_uchar * archive,t_uchar * version,t_uchar * pristine,t_uchar * changelog_loc,int full_meta)380 arch_finish_import (t_uchar * tree_root,
381                     t_uchar * archive,
382                     t_uchar * version,
383                     t_uchar * pristine,
384                     t_uchar * changelog_loc,
385                     int full_meta)
386 {
387   t_uchar * revision = 0;
388   t_uchar * pristine_dir = 0;
389   t_uchar * pristine_dir_tail = 0;
390 
391   revision = str_alloc_cat (0, version, "--base-0");
392 
393 
394   /****************************************************************
395    * txnally install the log file in the patch log
396    */
397   arch_finish_tree_commit (tree_root, archive, revision, changelog_loc, full_meta);
398 
399   /****************************************************************
400    * Install the pristine tree unless we have a greedy library
401    * that is eager to slurp it up.
402    *
403    * If we don't snarf the library here, it'll be deleted
404    * during "clean up", below.
405    */
406 
407   if (!arch_greedy_library_wants_revision (archive, revision))
408     arch_install_pristine (tree_root, archive, revision, pristine);
409 
410   /****************************************************************
411    * clean up
412    */
413   pristine_dir = file_name_directory_file (0, pristine);
414   invariant (!!pristine_dir);
415   pristine_dir_tail = file_name_tail (0, pristine_dir);
416   invariant (!str_cmp_prefix (",,import.", pristine_dir_tail));
417   rmrf_file (pristine_dir);
418 
419   lim_free (0, revision);
420   lim_free (0, pristine_dir);
421   lim_free (0, pristine_dir_tail);
422 }
423 
424 static void
arch_import_failed(t_uchar * tree_root,t_uchar * archive,t_uchar * version,t_uchar * pristine)425 arch_import_failed (t_uchar * tree_root, t_uchar * archive, t_uchar * version, t_uchar * pristine)
426 {
427   t_uchar * revision = 0;
428   t_uchar * pristine_dir = 0;
429   t_uchar * pristine_dir_tail = 0;
430 
431   revision = str_alloc_cat (0, version, "--base-0");
432 
433   /****************************************************************
434    * get out of mid-commit state in the project tree
435    */
436   arch_abort_tree_commit (tree_root, archive, revision);
437 
438   /****************************************************************
439    * clean up
440    */
441   pristine_dir = file_name_directory_file (0, pristine);
442   invariant (!!pristine_dir);
443   pristine_dir_tail = file_name_tail (0, pristine_dir);
444   invariant (str_cmp_prefix (",,import.", pristine_dir_tail));
445   rmrf_file (pristine_dir);
446 
447   lim_free (0, revision);
448   lim_free (0, pristine_dir);
449   lim_free (0, pristine_dir_tail);
450 }
451 
452 
453 
454 
455 
456 static rel_table
all_patches(t_uchar * tree_root)457 all_patches (t_uchar * tree_root)
458 {
459   rel_table log_versions = rel_table_nil;
460   rel_table answer = rel_table_nil;
461   int x;
462 
463   log_versions = arch_log_versions (tree_root, 0, 0, 0, 0);
464 
465   for (x = 0; x < rel_n_records (log_versions); ++x)
466     {
467       t_uchar * archive = 0;
468       t_uchar * version = 0;
469       rel_table patch_list = rel_table_nil;
470 
471       archive = arch_parse_package_name (arch_ret_archive, 0, rel_peek_str (log_versions, x, 0));
472       version = arch_parse_package_name (arch_ret_non_archive, 0, rel_peek_str (log_versions, x, 0));
473 
474       patch_list = arch_logs (tree_root, archive, version, 1);
475       rel_append_x (&answer, patch_list);
476 
477       lim_free (0, archive);
478       lim_free (0, version);
479       rel_free_table (patch_list);
480     }
481 
482   rel_free_table (log_versions);
483   return answer;
484 }
485 
486 
487 
488 
489 
490 /* tag: Tom Lord Sat May 24 22:40:45 2003 (import.c)
491  */
492