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