1 #include "builtin.h"
2 #include "parse-options.h"
3 #include "strbuf.h"
4 #include "help.h"
5 #include "compat/compiler.h"
6 #include "hook.h"
7 #include "hook-list.h"
8 
9 
get_system_info(struct strbuf * sys_info)10 static void get_system_info(struct strbuf *sys_info)
11 {
12 	struct utsname uname_info;
13 	char *shell = NULL;
14 
15 	/* get git version from native cmd */
16 	strbuf_addstr(sys_info, _("git version:\n"));
17 	get_version_info(sys_info, 1);
18 
19 	/* system call for other version info */
20 	strbuf_addstr(sys_info, "uname: ");
21 	if (uname(&uname_info))
22 		strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
23 			    strerror(errno),
24 			    errno);
25 	else
26 		strbuf_addf(sys_info, "%s %s %s %s\n",
27 			    uname_info.sysname,
28 			    uname_info.release,
29 			    uname_info.version,
30 			    uname_info.machine);
31 
32 	strbuf_addstr(sys_info, _("compiler info: "));
33 	get_compiler_info(sys_info);
34 
35 	strbuf_addstr(sys_info, _("libc info: "));
36 	get_libc_info(sys_info);
37 
38 	shell = getenv("SHELL");
39 	strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
40 		    shell ? shell : "<unset>");
41 }
42 
get_populated_hooks(struct strbuf * hook_info,int nongit)43 static void get_populated_hooks(struct strbuf *hook_info, int nongit)
44 {
45 	const char **p;
46 
47 	if (nongit) {
48 		strbuf_addstr(hook_info,
49 			_("not run from a git repository - no hooks to show\n"));
50 		return;
51 	}
52 
53 	for (p = hook_name_list; *p; p++) {
54 		const char *hook = *p;
55 
56 		if (hook_exists(hook))
57 			strbuf_addf(hook_info, "%s\n", hook);
58 	}
59 }
60 
61 static const char * const bugreport_usage[] = {
62 	N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
63 	NULL
64 };
65 
get_bug_template(struct strbuf * template)66 static int get_bug_template(struct strbuf *template)
67 {
68 	const char template_text[] = N_(
69 "Thank you for filling out a Git bug report!\n"
70 "Please answer the following questions to help us understand your issue.\n"
71 "\n"
72 "What did you do before the bug happened? (Steps to reproduce your issue)\n"
73 "\n"
74 "What did you expect to happen? (Expected behavior)\n"
75 "\n"
76 "What happened instead? (Actual behavior)\n"
77 "\n"
78 "What's different between what you expected and what actually happened?\n"
79 "\n"
80 "Anything else you want to add:\n"
81 "\n"
82 "Please review the rest of the bug report below.\n"
83 "You can delete any lines you don't wish to share.\n");
84 
85 	strbuf_addstr(template, _(template_text));
86 	return 0;
87 }
88 
get_header(struct strbuf * buf,const char * title)89 static void get_header(struct strbuf *buf, const char *title)
90 {
91 	strbuf_addf(buf, "\n\n[%s]\n", title);
92 }
93 
cmd_bugreport(int argc,const char ** argv,const char * prefix)94 int cmd_bugreport(int argc, const char **argv, const char *prefix)
95 {
96 	struct strbuf buffer = STRBUF_INIT;
97 	struct strbuf report_path = STRBUF_INIT;
98 	int report = -1;
99 	time_t now = time(NULL);
100 	struct tm tm;
101 	char *option_output = NULL;
102 	char *option_suffix = "%Y-%m-%d-%H%M";
103 	const char *user_relative_path = NULL;
104 	char *prefixed_filename;
105 
106 	const struct option bugreport_options[] = {
107 		OPT_STRING('o', "output-directory", &option_output, N_("path"),
108 			   N_("specify a destination for the bugreport file")),
109 		OPT_STRING('s', "suffix", &option_suffix, N_("format"),
110 			   N_("specify a strftime format suffix for the filename")),
111 		OPT_END()
112 	};
113 
114 	argc = parse_options(argc, argv, prefix, bugreport_options,
115 			     bugreport_usage, 0);
116 
117 	/* Prepare the path to put the result */
118 	prefixed_filename = prefix_filename(prefix,
119 					    option_output ? option_output : "");
120 	strbuf_addstr(&report_path, prefixed_filename);
121 	strbuf_complete(&report_path, '/');
122 
123 	strbuf_addstr(&report_path, "git-bugreport-");
124 	strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
125 	strbuf_addstr(&report_path, ".txt");
126 
127 	switch (safe_create_leading_directories(report_path.buf)) {
128 	case SCLD_OK:
129 	case SCLD_EXISTS:
130 		break;
131 	default:
132 		die(_("could not create leading directories for '%s'"),
133 		    report_path.buf);
134 	}
135 
136 	/* Prepare the report contents */
137 	get_bug_template(&buffer);
138 
139 	get_header(&buffer, _("System Info"));
140 	get_system_info(&buffer);
141 
142 	get_header(&buffer, _("Enabled Hooks"));
143 	get_populated_hooks(&buffer, !startup_info->have_repository);
144 
145 	/* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
146 	report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
147 
148 	if (write_in_full(report, buffer.buf, buffer.len) < 0)
149 		die_errno(_("unable to write to %s"), report_path.buf);
150 
151 	close(report);
152 
153 	/*
154 	 * We want to print the path relative to the user, but we still need the
155 	 * path relative to us to give to the editor.
156 	 */
157 	if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
158 		user_relative_path = report_path.buf;
159 	fprintf(stderr, _("Created new report at '%s'.\n"),
160 		user_relative_path);
161 
162 	free(prefixed_filename);
163 	UNLEAK(buffer);
164 	UNLEAK(report_path);
165 	return !!launch_editor(report_path.buf, NULL, NULL);
166 }
167