1 #include <sys/param.h>
2 #include <sys/stat.h>
3 #include <sys/types.h>
4 #include <sys/wait.h>
5
6 #include <ctype.h>
7 #include <curses.h>
8 #include <dialog.h>
9 #include <errno.h>
10 #include <glob.h>
11 #include <search.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stringlist.h>
17 #include <sysexits.h>
18 #include <unistd.h>
19
20 static int menulen=0;
21
22 typedef DIALOG_LISTITEM ListItem;
23 ListItem *menu;
24 #define ITEM_DATA help
25 #define ITEM_PROMPT name
26 #define ITEM_CHECKED state
27
28 static void
free_menu(void)29 free_menu(void)
30 {
31 int i;
32
33 if(menu) {
34 for(i=0; i<menulen; i++) {
35 if(menu[i].ITEM_DATA) {
36 free(menu[i].ITEM_DATA);
37 menu[i].ITEM_DATA=NULL;
38 }
39 if(menu[i].ITEM_PROMPT) {
40 free(menu[i].ITEM_PROMPT);
41 menu[i].ITEM_PROMPT=NULL;
42 }
43 }
44 free(menu);
45 }
46 menu=NULL;
47 menulen=0;
48 }
49
50 static int
blacklist(char * path,int operation)51 blacklist(char *path, int operation)
52 {
53 ENTRY item;
54
55 item.data=NULL;
56 if (operation == ENTER)
57 item.key=strdup(path);
58 else
59 item.key = path;
60
61 return(hsearch(item, operation)!=NULL);
62 }
63
64 static char *
read_desc(char * path)65 read_desc(char *path)
66 {
67 char newpath[MAXPATHLEN+1];
68 char *p=NULL;
69 char *np=NULL;
70 FILE *pkg;
71 char chunk[1024];
72 size_t cs, len=0;
73
74 sprintf(newpath,"/usr/sbin/pkg query %%e \"%s\"", path);
75 fflush(stdout);
76 pkg=popen(newpath, "r");
77 if(!pkg)
78 return NULL;
79 while((cs=fread(chunk, 1, sizeof(chunk), pkg))) {
80 np=(char *)realloc(p, len+cs+1);
81 if(!np) {
82 pclose(pkg);
83 free(p);
84 return NULL;
85 }
86 p=np;
87 memcpy(p+len, chunk, cs);
88 len+=cs;
89 p[len]=0;
90 }
91 pclose(pkg);
92 if(len==0) {
93 free(p);
94 return NULL;
95 }
96 return p;
97 }
98
99 /* Read +COMMENT, add to menu and blacklist */
100 static int
add_item(char * path,char * comment)101 add_item(char *path, char *comment)
102 {
103 char *p;
104 ListItem *newmenu;
105
106 if(blacklist(path,FIND))
107 return(0);
108 blacklist(path,ENTER);
109
110 newmenu=(ListItem *)realloc(menu, sizeof(ListItem)*(menulen+1));
111 if(newmenu==NULL)
112 return(-1);
113 menu=newmenu;
114
115 p=strrchr(path,'/');
116 if(p==NULL)
117 p=path;
118 else
119 p++;
120
121 menu[menulen].ITEM_PROMPT=strdup(p);
122 menu[menulen].ITEM_CHECKED=0;
123 menu[menulen].text="";
124
125 menu[menulen].ITEM_DATA=strdup(comment);
126 menulen++;
127
128 return(0);
129 }
130
131 static void
do_init_dialog(void)132 do_init_dialog(void)
133 {
134 init_dialog(stdin, stdout);
135 dialog_state.use_shadow=FALSE;
136 }
137
138 static int
display_menu(void)139 display_menu(void)
140 {
141 int ret=0;
142 int maxx,maxy;
143 int curr;
144
145 do_init_dialog();
146 getmaxyx(stdscr, maxy, maxx);
147 dialog_vars.help_button=1;
148 dialog_vars.item_help=1;
149 loop:
150 switch(ret=dlg_checklist("Welcome to pkg_cleanup.", "These are the leaf packages installed on your system\nChose the packages to deinstall. Help will display the package description.", maxy-1, maxx, maxy-9, menulen, menu, " X", FLAG_CHECK, &curr)) {
151 case DLG_EXIT_HELP: {
152 char *p = read_desc(menu[curr].ITEM_PROMPT);
153 if(p) {
154 dialog_vars.help_button=0;
155 dialog_msgbox(menu[curr].ITEM_DATA, p, maxy-4, maxx-4, TRUE);
156 dialog_vars.help_button=1;
157 free(p);
158 }
159 goto loop;
160 }
161 case 0:
162 ret=0;
163 break;
164 default:
165 ret=-1;
166 break;
167 }
168 dialog_vars.help_button=0;
169 dialog_vars.item_help=0;
170 end_dialog();
171 return(ret);
172 }
173
174 static int
remove_packages(void)175 remove_packages(void)
176 {
177 char **args;
178 char *env[1]={NULL};
179 char **p;
180 int i;
181 int delete_count=0;
182 pid_t child;
183 int status;
184 size_t st;
185
186 args=(char **)malloc((menulen+4) * sizeof(char *));
187 if(!args) {
188 do_init_dialog();
189 dialog_msgbox("ERROR", "Can not allocate memory for package list!", 5, 45, TRUE);
190 end_dialog();
191 return(-1);
192 }
193 p=args;
194 *(p++)="/usr/sbin/pkg";
195 *(p++)="delete";
196 *(p++)="-y";
197 for(i=0;i<menulen;i++) {
198 if(menu[i].ITEM_CHECKED) {
199 *(p++)=menu[i].ITEM_PROMPT;
200 delete_count++;
201 }
202 }
203 *(p)=NULL;
204 if(!delete_count) {
205 do_init_dialog();
206 dialog_msgbox(NULL, "No packages selected", 5, 24, TRUE);
207 end_dialog();
208 free(args);
209 return(-1);
210 }
211 /* Ensure stdio is "right" */
212 if(!isendwin())
213 endwin();
214 child=vfork();
215 if(child==0) {
216 execve(args[0], args, env);
217 fprintf(stderr,"ERROR: %d!\n",errno);
218 _exit(EXIT_FAILURE);
219 }
220 free(args);
221 if(child==-1) {
222 do_init_dialog();
223 dialog_msgbox(NULL, "Can not fork()", 5, 18, TRUE);
224 end_dialog();
225 return(-1);
226 }
227 waitpid(child, &status, 0);
228 /* On an error return, pause */
229 if(WIFSIGNALED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) {
230 fputs("\n\nPress ENTER to continue... ",stderr);
231 fgetln(stdin, &st);
232 }
233
234 return(0);
235 }
236
237 /* Goes through the glob contents, looking for zero-length +REQUIRED-BY */
238 static int
read_pkglist(int loops)239 read_pkglist(int loops)
240 {
241 FILE *pkg;
242 char line[1024];
243 char *p;
244
245 do_init_dialog();
246 dialog_msgbox(NULL, "Searching for leaves", 5, 23, FALSE);
247 fflush(stdout);
248 pkg=popen("/usr/sbin/pkg query -e \"%#r=0 && %k=0\" \"%n-%v\\t%c\"", "r");
249 if(!pkg) {
250 fputs("Error executing pkg.\n", stderr);
251 return -1;
252 }
253 while(fgets(line, sizeof(line), pkg)!=NULL) {
254 for(p=strchr(line, 0)-1; p>line && isspace(*p); p--)
255 *p=0;
256 p=strtok(line, "\t");
257 p=strtok(NULL, "\0");
258 add_item(line, p?p:"");
259 }
260 if(WEXITSTATUS(pclose(pkg)) && menulen==0) {
261 fputs("pkg returned an error.\n", stderr);
262 return -1;
263 }
264 dlg_clear();
265 if(menulen==0) {
266 if(loops)
267 dialog_msgbox(NULL, "No new leaves found", 5, 23, TRUE);
268 else
269 dialog_msgbox(NULL, "No leaves found", 5, 19, TRUE);
270 end_dialog();
271 return(-1);
272 }
273 end_dialog();
274 return(0);
275 }
276
277 static int
keep_going(void)278 keep_going(void)
279 {
280 int ret;
281
282 do_init_dialog();
283 ret = !dialog_yesno(NULL,"Do you want to process the new leaves packages?",-1,-1);
284 end_dialog();
285 return(ret);
286 }
287
288 /*
289 * The hash table is to keep track of leaves which have already been displayed
290 * to the user.
291 */
main(int argc,char ** argv)292 int main(int argc, char **argv)
293 {
294 int i;
295
296 /* Initialize space for blacklist hash table */
297 if(hcreate(10000)==0) {
298 fputs("Cannot create hash table.\n",stderr);
299 return(EX_UNAVAILABLE);
300 }
301
302 i=0;
303 do {
304 if(read_pkglist(i++))
305 break;
306 if(display_menu())
307 break;
308 if(remove_packages())
309 break;
310 free_menu();
311 } while(keep_going());
312 hdestroy();
313
314 fputs("\nProgram Terminated Successfully\n",stderr);
315 return(0);
316 }
317