1 /*
2  *
3  * Conky, a system monitor, based on torsmo
4  *
5  * Any original torsmo code is licensed under the BSD license
6  *
7  * All code written since the fork of torsmo is licensed under the GPL
8  *
9  * Please see COPYING for details
10  *
11  * Copyright (c) 2005 Adi Zaimi, Dan Piponi <dan@tanelorn.demon.co.uk>,
12  *					  Dave Clark <clarkd@skynet.ca>
13  * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
14  *	(see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 
31 #include "top.h"
32 #include "logging.h"
33 #include "prioqueue.h"
34 
35 /* hash table size - always a power of 2 */
36 #define HTABSIZE 256
37 
38 struct process *first_process = nullptr;
39 
40 unsigned long g_time = 0;
41 
42 /* a simple hash table to speed up find_process() */
43 struct proc_hash_entry {
44   struct proc_hash_entry *next;
45   struct process *proc;
46 };
47 static struct proc_hash_entry proc_hash_table[HTABSIZE];
48 
hash_process(struct process * p)49 static void hash_process(struct process *p) {
50   struct proc_hash_entry *phe;
51   static char first_run = 1;
52   int bucket;
53 
54   /* better make sure all next pointers are zero upon first access */
55   if (first_run != 0) {
56     memset(proc_hash_table, 0, sizeof(struct proc_hash_entry) * HTABSIZE);
57     first_run = 0;
58   }
59 
60   /* get the bucket index */
61   bucket = p->pid & (HTABSIZE - 1);
62 
63   /* insert a new element on bucket's top */
64   phe = static_cast<struct proc_hash_entry *>(
65       malloc(sizeof(struct proc_hash_entry)));
66   phe->proc = p;
67   phe->next = proc_hash_table[bucket].next;
68   proc_hash_table[bucket].next = phe;
69 }
70 
unhash_process(struct process * p)71 static void unhash_process(struct process *p) {
72   struct proc_hash_entry *phe, *tmp;
73 
74   /* get the bucket head */
75   phe = &proc_hash_table[p->pid & (HTABSIZE - 1)];
76   /* find the entry pointing to p and drop it */
77   while (phe->next != nullptr) {
78     if (phe->next->proc == p) {
79       tmp = phe->next;
80       phe->next = phe->next->next;
81       free(tmp);
82       return;
83     }
84     phe = phe->next;
85   }
86 }
87 
__unhash_all_processes(struct proc_hash_entry * phe)88 static void __unhash_all_processes(struct proc_hash_entry *phe) {
89   if (phe->next != nullptr) { __unhash_all_processes(phe->next); }
90   free(phe->next);
91 }
92 
unhash_all_processes()93 static void unhash_all_processes() {
94   int i;
95 
96   for (i = 0; i < HTABSIZE; i++) {
97     __unhash_all_processes(&proc_hash_table[i]);
98     proc_hash_table[i].next = nullptr;
99   }
100 }
101 
get_first_process()102 struct process *get_first_process() {
103   return first_process;
104 }
105 
free_all_processes()106 void free_all_processes() {
107   struct process *next = nullptr, *pr = first_process;
108 
109   while (pr != nullptr) {
110     next = pr->next;
111     free_and_zero(pr->name);
112     free_and_zero(pr->basename);
113     free(pr);
114     pr = next;
115   }
116   first_process = nullptr;
117 
118   /* drop the whole hash table */
119   unhash_all_processes();
120 }
121 
get_process_by_name(const char * name)122 struct process *get_process_by_name(const char *name) {
123   struct process *p = first_process;
124 
125   while (p != nullptr) {
126     /* Try matching against the full command line first. If that fails,
127      * fall back to the basename.
128      */
129     if (((p->name != nullptr) && (strcmp(p->name, name) == 0)) ||
130         ((p->basename != nullptr) && (strcmp(p->basename, name) == 0))) {
131       return p;
132     }
133     p = p->next;
134   }
135   return nullptr;
136 }
137 
find_process(pid_t pid)138 static struct process *find_process(pid_t pid) {
139   struct proc_hash_entry *phe;
140 
141   phe = &proc_hash_table[pid & (HTABSIZE - 1)];
142   while (phe->next != nullptr) {
143     if (phe->next->proc->pid == pid) { return phe->next->proc; }
144     phe = phe->next;
145   }
146   return nullptr;
147 }
148 
new_process(pid_t pid)149 static struct process *new_process(pid_t pid) {
150   auto *p = static_cast<struct process *>(malloc(sizeof(struct process)));
151 
152   /* Do stitching necessary for doubly linked list */
153   p->previous = nullptr;
154   p->next = first_process;
155   if (p->next != nullptr) { p->next->previous = p; }
156   first_process = p;
157 
158   p->pid = pid;
159   p->name = nullptr;
160   p->basename = nullptr;
161   p->amount = 0;
162   p->user_time = 0;
163   p->total = 0;
164   p->kernel_time = 0;
165   p->previous_user_time = ULONG_MAX;
166   p->previous_kernel_time = ULONG_MAX;
167   p->total_cpu_time = 0;
168   p->vsize = 0;
169   p->rss = 0;
170 #ifdef BUILD_IOSTATS
171   p->read_bytes = 0;
172   p->previous_read_bytes = ULLONG_MAX;
173   p->write_bytes = 0;
174   p->previous_write_bytes = ULLONG_MAX;
175   p->io_perc = 0;
176 #endif /* BUILD_IOSTATS */
177   p->time_stamp = 0;
178   p->counted = 1;
179   p->changed = 0;
180 
181   /* process_find_name(p); */
182 
183   /* add the process to the hash table */
184   hash_process(p);
185 
186   return p;
187 }
188 
189 /* Get / create a new process object and insert it into the process list */
get_process(pid_t pid)190 struct process *get_process(pid_t pid) {
191   struct process *p = find_process(pid);
192   return p != nullptr ? p : new_process(pid);
193 }
194 
195 /******************************************
196  * Functions							  *
197  ******************************************/
198 
199 /******************************************
200  * Destroy and remove a process           *
201  ******************************************/
202 
delete_process(struct process * p)203 static void delete_process(struct process *p) {
204 #if defined(PARANOID)
205   assert(p->id == 0x0badfeed);
206 
207   /*
208    * Ensure that deleted processes aren't reused.
209    */
210   p->id = 0x007babe;
211 #endif /* defined(PARANOID) */
212 
213   /*
214    * Maintain doubly linked list.
215    */
216   if (p->next != nullptr) { p->next->previous = p->previous; }
217   if (p->previous != nullptr) {
218     p->previous->next = p->next;
219   } else {
220     first_process = p->next;
221   }
222 
223   free_and_zero(p->name);
224   free_and_zero(p->basename);
225   /* remove the process from the hash table */
226   unhash_process(p);
227   free(p);
228 }
229 
230 /******************************************
231  * Strip dead process entries			  *
232  ******************************************/
233 
process_cleanup()234 static void process_cleanup() {
235   struct process *p = first_process;
236 
237   while (p != nullptr) {
238     struct process *current = p;
239 
240 #if defined(PARANOID)
241     assert(p->id == 0x0badfeed);
242 #endif /* defined(PARANOID) */
243 
244     p = p->next;
245     /* Delete processes that have died */
246     if (current->time_stamp != g_time) {
247       delete_process(current);
248       if (current == first_process) { first_process = nullptr; }
249       current = nullptr;
250     }
251   }
252 }
253 
254 /******************************************
255  * Find the top processes				  *
256  ******************************************/
257 
258 /* cpu comparison function for prio queue */
compare_cpu(void * va,void * vb)259 static int compare_cpu(void *va, void *vb) {
260   auto *a = static_cast<struct process *>(va),
261        *b = static_cast<struct process *>(vb);
262 
263   if (b->amount > a->amount) { return 1; }
264   if (a->amount > b->amount) { return -1; }
265   return 0;
266 }
267 
268 /* mem comparison function for prio queue */
compare_mem(void * va,void * vb)269 static int compare_mem(void *va, void *vb) {
270   auto *a = static_cast<struct process *>(va),
271        *b = static_cast<struct process *>(vb);
272 
273   if (b->rss > a->rss) { return 1; }
274   if (a->rss > b->rss) { return -1; }
275   return 0;
276 }
277 
278 /* CPU time comparison function for prio queue */
compare_time(void * va,void * vb)279 static int compare_time(void *va, void *vb) {
280   auto *a = static_cast<struct process *>(va),
281        *b = static_cast<struct process *>(vb);
282 
283   if (b->total_cpu_time > a->total_cpu_time) { return 1; }
284   if (b->total_cpu_time < a->total_cpu_time) { return -1; }
285   return 0;
286 }
287 
288 #ifdef BUILD_IOSTATS
289 /* I/O comparison function for prio queue */
compare_io(void * va,void * vb)290 static int compare_io(void *va, void *vb) {
291   auto *a = static_cast<struct process *>(va),
292        *b = static_cast<struct process *>(vb);
293 
294   if (b->io_perc > a->io_perc) { return 1; }
295   if (a->io_perc > b->io_perc) { return -1; }
296   return 0;
297 }
298 #endif /* BUILD_IOSTATS */
299 
300 /* ****************************************************************** *
301  * Get a sorted list of the top cpu hogs and top mem hogs. * Results are stored
302  * in the cpu,mem arrays in decreasing order[0-9]. *
303  * ****************************************************************** */
304 
process_find_top(struct process ** cpu,struct process ** mem,struct process ** ptime,struct process ** io)305 static void process_find_top(struct process **cpu, struct process **mem,
306                              struct process **ptime
307 #ifdef BUILD_IOSTATS
308                              ,
309                              struct process **io
310 #endif /* BUILD_IOSTATS */
311 ) {
312   prio_queue_t cpu_queue, mem_queue, time_queue;
313 #ifdef BUILD_IOSTATS
314   prio_queue_t io_queue;
315 #endif
316   struct process *cur_proc = nullptr;
317   int i;
318 
319   if ((top_cpu == 0) && (top_mem == 0) && (top_time == 0)
320 #ifdef BUILD_IOSTATS
321       && (top_io == 0)
322 #endif /* BUILD_IOSTATS */
323       && (top_running == 0)) {
324     return;
325   }
326 
327   cpu_queue = init_prio_queue();
328   pq_set_compare(cpu_queue, &compare_cpu);
329   pq_set_max_size(cpu_queue, MAX_SP);
330 
331   mem_queue = init_prio_queue();
332   pq_set_compare(mem_queue, &compare_mem);
333   pq_set_max_size(mem_queue, MAX_SP);
334 
335   time_queue = init_prio_queue();
336   pq_set_compare(time_queue, &compare_time);
337   pq_set_max_size(time_queue, MAX_SP);
338 
339 #ifdef BUILD_IOSTATS
340   io_queue = init_prio_queue();
341   pq_set_compare(io_queue, &compare_io);
342   pq_set_max_size(io_queue, MAX_SP);
343 #endif
344 
345   /* g_time is the time_stamp entry for process.  It is updated when the
346    * process information is updated to indicate that the process is still
347    * alive (and must not be removed from the process list in
348    * process_cleanup()) */
349   ++g_time;
350 
351   /* OS-specific function updating process list */
352   get_top_info();
353 
354   process_cleanup(); /* cleanup list from exited processes */
355 
356   cur_proc = first_process;
357 
358   while (cur_proc != nullptr) {
359     if (top_cpu != 0) { insert_prio_elem(cpu_queue, cur_proc); }
360     if (top_mem != 0) { insert_prio_elem(mem_queue, cur_proc); }
361     if (top_time != 0) { insert_prio_elem(time_queue, cur_proc); }
362 #ifdef BUILD_IOSTATS
363     if (top_io != 0) { insert_prio_elem(io_queue, cur_proc); }
364 #endif /* BUILD_IOSTATS */
365     cur_proc = cur_proc->next;
366   }
367 
368   for (i = 0; i < MAX_SP; i++) {
369     if (top_cpu != 0) {
370       cpu[i] = static_cast<process *>(pop_prio_elem(cpu_queue));
371     }
372     if (top_mem != 0) {
373       mem[i] = static_cast<process *>(pop_prio_elem(mem_queue));
374     }
375     if (top_time != 0) {
376       ptime[i] = static_cast<process *>(pop_prio_elem(time_queue));
377     }
378 #ifdef BUILD_IOSTATS
379     if (top_io != 0) {
380       io[i] = static_cast<process *>(pop_prio_elem(io_queue));
381     }
382 #endif /* BUILD_IOSTATS */
383   }
384   free_prio_queue(cpu_queue);
385   free_prio_queue(mem_queue);
386   free_prio_queue(time_queue);
387 #ifdef BUILD_IOSTATS
388   free_prio_queue(io_queue);
389 #endif /* BUILD_IOSTATS */
390 }
391 
update_top()392 int update_top() {
393   process_find_top(info.cpu, info.memu, info.time
394 #ifdef BUILD_IOSTATS
395                    ,
396                    info.io
397 #endif
398   );
399   info.first_process = get_first_process();
400   return 0;
401 }
402 
format_time(unsigned long timeval,const int width)403 static char *format_time(unsigned long timeval, const int width) {
404   char buf[10];
405   unsigned long nt;  // narrow time, for speed on 32-bit
406   unsigned cc;       // centiseconds
407   unsigned nn;       // multi-purpose whatever
408 
409   nt = timeval;
410   cc = nt % 100;  // centiseconds past second
411   nt /= 100;      // total seconds
412   nn = nt % 60;   // seconds past the minute
413   nt /= 60;       // total minutes
414   if (width >= snprintf(buf, sizeof buf, "%lu:%02u.%02u", nt, nn, cc)) {
415     return strndup(buf, text_buffer_size.get(*state));
416   }
417   if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) {
418     return strndup(buf, text_buffer_size.get(*state));
419   }
420   nn = nt % 60;  // minutes past the hour
421   nt /= 60;      // total hours
422   if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) {
423     return strndup(buf, text_buffer_size.get(*state));
424   }
425   nn = nt;  // now also hours
426   if (width >= snprintf(buf, sizeof buf, "%uh", nn)) {
427     return strndup(buf, text_buffer_size.get(*state));
428   }
429   nn /= 24;  // now days
430   if (width >= snprintf(buf, sizeof buf, "%ud", nn)) {
431     return strndup(buf, text_buffer_size.get(*state));
432   }
433   nn /= 7;  // now weeks
434   if (width >= snprintf(buf, sizeof buf, "%uw", nn)) {
435     return strndup(buf, text_buffer_size.get(*state));
436   }
437   // well shoot, this outta' fit...
438   return strndup("<inf>", text_buffer_size.get(*state));
439 }
440 
441 struct top_data {
442   struct process **list;
443   int num;
444   int was_parsed;
445   char *s;
446 };
447 
448 static conky::range_config_setting<unsigned int> top_name_width(
449     "top_name_width", 0, std::numeric_limits<unsigned int>::max(), 15, true);
450 static conky::simple_config_setting<bool> top_name_verbose("top_name_verbose",
451                                                            false, true);
452 
print_top_name(struct text_object * obj,char * p,unsigned int p_max_size)453 static void print_top_name(struct text_object *obj, char *p,
454                            unsigned int p_max_size) {
455   auto *td = static_cast<struct top_data *>(obj->data.opaque);
456   int width;
457 
458   if ((td == nullptr) || (td->list == nullptr) ||
459       (td->list[td->num] == nullptr)) {
460     return;
461   }
462 
463   width = std::min(p_max_size,
464                    static_cast<unsigned int>(top_name_width.get(*state)) + 1);
465   if (top_name_verbose.get(*state)) {
466     /* print the full command line */
467     snprintf(p, width + 1, "%-*s", width, td->list[td->num]->name);
468   } else {
469     /* print only the basename (i.e. executable name) */
470     snprintf(p, width + 1, "%-*s", width, td->list[td->num]->basename);
471   }
472 }
473 
print_top_mem(struct text_object * obj,char * p,unsigned int p_max_size)474 static void print_top_mem(struct text_object *obj, char *p,
475                           unsigned int p_max_size) {
476   auto *td = static_cast<struct top_data *>(obj->data.opaque);
477   int width;
478 
479   if ((td == nullptr) || (td->list == nullptr) ||
480       (td->list[td->num] == nullptr)) {
481     return;
482   }
483 
484   width = std::min(p_max_size, static_cast<unsigned int>(7));
485   snprintf(p, width, "%6.2f",
486            (static_cast<float>(td->list[td->num]->rss) / info.memmax) / 10);
487 }
488 
print_top_time(struct text_object * obj,char * p,unsigned int p_max_size)489 static void print_top_time(struct text_object *obj, char *p,
490                            unsigned int p_max_size) {
491   auto *td = static_cast<struct top_data *>(obj->data.opaque);
492   int width;
493   char *timeval;
494 
495   if ((td == nullptr) || (td->list == nullptr) ||
496       (td->list[td->num] == nullptr)) {
497     return;
498   }
499 
500   width = std::min(p_max_size, static_cast<unsigned int>(10));
501   timeval = format_time(td->list[td->num]->total_cpu_time, 9);
502   snprintf(p, width, "%9s", timeval);
503   free(timeval);
504 }
505 
print_top_user(struct text_object * obj,char * p,unsigned int p_max_size)506 static void print_top_user(struct text_object *obj, char *p,
507                            unsigned int p_max_size) {
508   auto *td = static_cast<struct top_data *>(obj->data.opaque);
509   struct passwd *pw;
510 
511   if ((td == nullptr) || (td->list == nullptr) ||
512       (td->list[td->num] == nullptr)) {
513     return;
514   }
515 
516   pw = getpwuid(td->list[td->num]->uid);
517   if (pw != nullptr) {
518     snprintf(p, p_max_size, "%.8s", pw->pw_name);
519   } else {
520     snprintf(p, p_max_size, "%d", td->list[td->num]->uid);
521   }
522 }
523 
524 #define PRINT_TOP_GENERATOR(name, width, fmt, field)                         \
525   static void print_top_##name(struct text_object *obj, char *p,             \
526                                unsigned int p_max_size) {                    \
527     struct top_data *td = (struct top_data *)obj->data.opaque;               \
528     if (!td || !td->list || !td->list[td->num]) return;                      \
529     snprintf(p, std::min(p_max_size, width), fmt, td->list[td->num]->field); \
530   }
531 
532 #define PRINT_TOP_HR_GENERATOR(name, field, denom)                     \
533   static void print_top_##name(struct text_object *obj, char *p,       \
534                                unsigned int p_max_size) {              \
535     struct top_data *td = (struct top_data *)obj->data.opaque;         \
536     if (!td || !td->list || !td->list[td->num]) return;                \
537     human_readable(td->list[td->num]->field / (denom), p, p_max_size); \
538   }
539 
540 PRINT_TOP_GENERATOR(cpu, (unsigned int)7, "%6.2f", amount)
541 PRINT_TOP_GENERATOR(pid, (unsigned int)8, "%7i", pid)
542 PRINT_TOP_GENERATOR(uid, (unsigned int)6, "%5i", uid)
543 PRINT_TOP_HR_GENERATOR(mem_res, rss, 1)
544 PRINT_TOP_HR_GENERATOR(mem_vsize, vsize, 1)
545 #ifdef BUILD_IOSTATS
PRINT_TOP_HR_GENERATOR(read_bytes,read_bytes,active_update_interval ())546 PRINT_TOP_HR_GENERATOR(read_bytes, read_bytes, active_update_interval())
547 PRINT_TOP_HR_GENERATOR(write_bytes, write_bytes, active_update_interval())
548 PRINT_TOP_GENERATOR(io_perc, (unsigned int)7, "%6.2f", io_perc)
549 #endif /* BUILD_IOSTATS */
550 
551 static void free_top(struct text_object *obj) {
552   auto *td = static_cast<struct top_data *>(obj->data.opaque);
553 
554   if (td == nullptr) { return; }
555   free_and_zero(td->s);
556   free_and_zero(obj->data.opaque);
557 }
558 
parse_top_args(const char * s,const char * arg,struct text_object * obj)559 int parse_top_args(const char *s, const char *arg, struct text_object *obj) {
560   struct top_data *td;
561   char buf[64];
562   int n;
563 
564   if (arg == nullptr) {
565     NORM_ERR("top needs arguments");
566     return 0;
567   }
568 
569   obj->data.opaque = td =
570       static_cast<struct top_data *>(malloc(sizeof(struct top_data)));
571   memset(td, 0, sizeof(struct top_data));
572 
573   if (s[3] == 0) {
574     td->list = info.cpu;
575     top_cpu = 1;
576   } else if (strcmp(&s[3], "_mem") == EQUAL) {
577     td->list = info.memu;
578     top_mem = 1;
579   } else if (strcmp(&s[3], "_time") == EQUAL) {
580     td->list = info.time;
581     top_time = 1;
582 #ifdef BUILD_IOSTATS
583   } else if (strcmp(&s[3], "_io") == EQUAL) {
584     td->list = info.io;
585     top_io = 1;
586 #endif /* BUILD_IOSTATS */
587   } else {
588 #ifdef BUILD_IOSTATS
589     NORM_ERR("Must be top, top_mem, top_time or top_io");
590 #else  /* BUILD_IOSTATS */
591     NORM_ERR("Must be top, top_mem or top_time");
592 #endif /* BUILD_IOSTATS */
593     free_and_zero(obj->data.opaque);
594     return 0;
595   }
596 
597   td->s = strndup(arg, text_buffer_size.get(*state));
598 
599   if (sscanf(arg, "%63s %i", buf, &n) == 2) {
600     if (strcmp(buf, "name") == EQUAL) {
601       obj->callbacks.print = &print_top_name;
602     } else if (strcmp(buf, "cpu") == EQUAL) {
603       obj->callbacks.print = &print_top_cpu;
604     } else if (strcmp(buf, "pid") == EQUAL) {
605       obj->callbacks.print = &print_top_pid;
606     } else if (strcmp(buf, "mem") == EQUAL) {
607       obj->callbacks.print = &print_top_mem;
608     } else if (strcmp(buf, "time") == EQUAL) {
609       obj->callbacks.print = &print_top_time;
610     } else if (strcmp(buf, "mem_res") == EQUAL) {
611       obj->callbacks.print = &print_top_mem_res;
612     } else if (strcmp(buf, "mem_vsize") == EQUAL) {
613       obj->callbacks.print = &print_top_mem_vsize;
614     } else if (strcmp(buf, "uid") == EQUAL) {
615       obj->callbacks.print = &print_top_uid;
616     } else if (strcmp(buf, "user") == EQUAL) {
617       obj->callbacks.print = &print_top_user;
618 #ifdef BUILD_IOSTATS
619     } else if (strcmp(buf, "io_read") == EQUAL) {
620       obj->callbacks.print = &print_top_read_bytes;
621     } else if (strcmp(buf, "io_write") == EQUAL) {
622       obj->callbacks.print = &print_top_write_bytes;
623     } else if (strcmp(buf, "io_perc") == EQUAL) {
624       obj->callbacks.print = &print_top_io_perc;
625 #endif /* BUILD_IOSTATS */
626     } else {
627       NORM_ERR("invalid type arg for top");
628 #ifdef BUILD_IOSTATS
629       NORM_ERR(
630           "must be one of: name, cpu, pid, mem, time, mem_res, mem_vsize, "
631           "io_read, io_write, io_perc");
632 #else  /* BUILD_IOSTATS */
633       NORM_ERR("must be one of: name, cpu, pid, mem, time, mem_res, mem_vsize");
634 #endif /* BUILD_IOSTATS */
635       free_and_zero(td->s);
636       free_and_zero(obj->data.opaque);
637       return 0;
638     }
639     if (n < 1 || n > MAX_SP) {
640       NORM_ERR("invalid num arg for top. Must be between 1 and %d.", MAX_SP);
641       free_and_zero(td->s);
642       free_and_zero(obj->data.opaque);
643       return 0;
644     }
645     td->num = n - 1;
646 
647   } else {
648     NORM_ERR("invalid argument count for top");
649     free_and_zero(td->s);
650     free_and_zero(obj->data.opaque);
651     return 0;
652   }
653   obj->callbacks.free = &free_top;
654   return 1;
655 }
656