Skip to content
This repository was archived by the owner on May 24, 2020. It is now read-only.

Commit 1575cd3

Browse files
committed
Initial support for spawning grub-mkconfig for GRUB2
1 parent cf8409d commit 1575cd3

File tree

10 files changed

+328
-3
lines changed

10 files changed

+328
-3
lines changed

Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ bin_PROGRAMS = eclean-kernel
77
eclean_kernel_SOURCES = \
88
src/ek2/actions.cxx \
99
src/ek2/actions.h \
10+
src/ek2/bootloader.cxx \
11+
src/ek2/bootloader.h \
12+
src/ek2/bootloaders/grub2.cxx \
13+
src/ek2/bootloaders/grub2.h \
14+
src/ek2/bootloaders.cxx \
15+
src/ek2/bootloaders.h \
1016
src/ek2/file.cxx \
1117
src/ek2/file.h \
1218
src/ek2/files/builddir.cxx \

src/ek2/actions.cxx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ void list_kernels(const Layout& l)
3030
}
3131
}
3232

33-
void remove(Layout& l, const Options& opts)
33+
void remove(Layout& l, const Options& opts,
34+
std::vector<std::unique_ptr<BootLoader>>& bootloaders)
3435
{
3536
std::vector<std::unique_ptr<Judge>> judges = get_judges(opts);
3637
fileset_vote_map fileset_votes;
@@ -128,4 +129,7 @@ void remove(Layout& l, const Options& opts)
128129
}
129130
}
130131
}
132+
133+
for (std::unique_ptr<BootLoader>& b : bootloaders)
134+
b->postrm();
131135
}

src/ek2/actions.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
#define EK2_ACTIONS_H 1
1010

1111
#include "ek2/layout.h"
12+
#include "ek2/bootloader.h"
13+
14+
#include <memory>
15+
#include <vector>
1216

1317
void list_kernels(const Layout& l);
14-
void remove(Layout& l, const Options& opts);
18+
void remove(Layout& l, const Options& opts,
19+
std::vector<std::unique_ptr<BootLoader>>& bootloaders);
1520

1621
#endif /*EK2_ACTIONS_H*/

src/ek2/bootloader.cxx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#ifdef HAVE_CONFIG_H
7+
# include "config.h"
8+
#endif
9+
10+
#include "ek2/bootloader.h"
11+
12+
BootLoader::BootLoader(const Options& opts)
13+
: opts_(opts)
14+
{
15+
}
16+
17+
void BootLoader::postrm()
18+
{
19+
}

src/ek2/bootloader.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#pragma once
7+
8+
#ifndef EK2_BOOTLOADER_H
9+
#define EK2_BOOTLOADER_H 1
10+
11+
#include "ek2/options.h"
12+
13+
// an abstract interface to a bootloader
14+
class BootLoader
15+
{
16+
protected:
17+
const Options& opts_;
18+
BootLoader(const Options& opts);
19+
20+
public:
21+
// optional hook run after removing some kernels
22+
virtual void postrm();
23+
};
24+
25+
#endif /*EK2_BOOTLOADER_H*/

src/ek2/bootloaders.cxx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#ifdef HAVE_CONFIG_H
7+
# include "config.h"
8+
#endif
9+
10+
#include "ek2/bootloaders.h"
11+
12+
#include "ek2/bootloaders/grub2.h"
13+
14+
#include <cassert>
15+
#include <functional>
16+
#include <memory>
17+
#include <vector>
18+
19+
struct bootloader_info
20+
{
21+
std::string name;
22+
std::function<std::unique_ptr<BootLoader>(const Options& opts)> construct;
23+
};
24+
25+
typedef std::vector<bootloader_info> bootloader_list_type;
26+
27+
static const bootloader_list_type bootloader_list = {
28+
{ "grub2", GRUB2::construct },
29+
};
30+
31+
std::vector<std::unique_ptr<BootLoader>> get_bootloaders(const Options& opts)
32+
{
33+
std::vector<std::unique_ptr<BootLoader>> ret;
34+
35+
for (const bootloader_info& bi : bootloader_list)
36+
{
37+
std::unique_ptr<BootLoader> b = bi.construct(opts);
38+
if (b)
39+
ret.push_back(std::move(b));
40+
}
41+
42+
return ret;
43+
}

src/ek2/bootloaders.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#pragma once
7+
8+
#ifndef EK2_BOOTLOADERS_H
9+
#define EK2_BOOTLOADERS_H 1
10+
11+
#include "ek2/bootloader.h"
12+
#include "ek2/options.h"
13+
14+
#include <memory>
15+
#include <vector>
16+
17+
// Get supported bootloaders
18+
std::vector<std::unique_ptr<BootLoader>> get_bootloaders(const Options& opts);
19+
20+
#endif /*EK2_BOOTLOADERS_H*/

src/ek2/bootloaders/grub2.cxx

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#ifdef HAVE_CONFIG_H
7+
# include "config.h"
8+
#endif
9+
10+
#include "ek2/bootloaders/grub2.h"
11+
12+
#include "ek2/util/directorystream.h"
13+
#include "ek2/util/error.h"
14+
#include "ek2/util/relativepath.h"
15+
16+
#include <cerrno>
17+
#include <cstring>
18+
#include <iostream>
19+
#include <string>
20+
#include <vector>
21+
22+
extern "C"
23+
{
24+
# include <fcntl.h>
25+
# include <unistd.h>
26+
# include <sys/wait.h>
27+
};
28+
29+
static const char grub2_autogen_header[] =
30+
"#\n"
31+
"# DO NOT EDIT THIS FILE\n"
32+
"#\n"
33+
"# It is automatically generated by grub-mkconfig";
34+
35+
GRUB2::GRUB2(const Options& opts)
36+
: BootLoader(opts), is_autogenerated_(false), is_multislot_(false)
37+
{
38+
DirectoryStream topds;
39+
40+
try
41+
{
42+
topds.open(opts.boot_path);
43+
}
44+
catch (IOError& e)
45+
{
46+
if (e.err() != ENOENT)
47+
throw;
48+
return;
49+
}
50+
51+
// try /boot/grub/grub.cfg first
52+
try
53+
{
54+
grub_dir_.reset(new DirectoryStream(RelativePath(topds, "grub")));
55+
grub_cfg_.reset(new RelativePath(grub_dir_, "grub.cfg"));
56+
process_config();
57+
return;
58+
}
59+
catch (IOError& e)
60+
{
61+
if (e.err() != ENOENT)
62+
throw;
63+
}
64+
65+
// fallback to /boot/grub2/grub.cfg
66+
try
67+
{
68+
grub_dir_.reset(new DirectoryStream(RelativePath(topds, "grub2")));
69+
grub_cfg_.reset(new RelativePath(grub_dir_, "grub.cfg"));
70+
process_config();
71+
is_multislot_ = true;
72+
return;
73+
}
74+
catch (IOError& e)
75+
{
76+
if (e.err() != ENOENT)
77+
throw;
78+
}
79+
80+
// no success? make sure it's null again
81+
grub_cfg_.reset(nullptr);
82+
}
83+
84+
bool GRUB2::is_installed() const
85+
{
86+
return !!grub_cfg_;
87+
}
88+
89+
void GRUB2::process_config()
90+
{
91+
int fd = grub_cfg_->file_fd(O_RDONLY);
92+
93+
// check if the config is autogenerated
94+
char buf[sizeof(grub2_autogen_header)-1];
95+
ssize_t rd = read(fd, &buf, sizeof(buf));
96+
if (rd == -1)
97+
throw IOError("Reading failed on " + grub_cfg_->filename(), errno);
98+
if (rd == sizeof(buf) && !memcmp(buf, grub2_autogen_header, sizeof(buf)))
99+
is_autogenerated_ = true;
100+
}
101+
102+
std::unique_ptr<BootLoader> GRUB2::construct(const Options& opts)
103+
{
104+
std::unique_ptr<GRUB2> b{new GRUB2(opts)};
105+
106+
if (b->is_installed())
107+
return std::move(b);
108+
109+
return nullptr;
110+
}
111+
112+
void GRUB2::postrm()
113+
{
114+
// if the config is autogenerated, and we removed some kernels
115+
// we should update it
116+
if (is_autogenerated_)
117+
{
118+
// TODO: move this to util/
119+
// love the C exec*() API
120+
std::vector<std::string> argv{"grub-mkconfig",
121+
is_multislot_ ? "grub2-mkconfig" : "grub-mkconfig",
122+
"-o", grub_cfg_->path()};
123+
std::vector<std::vector<char>> argv_rw;
124+
std::vector<char*> cargv;
125+
126+
if (opts_.pretend)
127+
{
128+
std::cerr << "\nThe following command would be run: "
129+
<< argv[1] << " " << argv[2] << " " << argv[3] << std::endl;
130+
return;
131+
}
132+
133+
std::cerr << "\nRunning: " << argv[1] << " " << argv[2]
134+
<< " " << argv[3] << std::endl;
135+
136+
for (const std::string& s : argv)
137+
{
138+
// std::string -> std::vector<char>
139+
argv_rw.emplace_back(s.begin(), s.end());
140+
argv_rw.back().push_back('\0');
141+
142+
// std::vector<char> -> char*
143+
cargv.push_back(argv_rw.back().data());
144+
}
145+
// sentinel
146+
cargv.push_back(nullptr);
147+
148+
pid_t child_pid = fork();
149+
switch (child_pid)
150+
{
151+
case -1:
152+
throw IOError("fork() failed", errno);
153+
break;
154+
case 0:
155+
std::cerr << cargv[0] << "\n";
156+
execvp(cargv[0], cargv.data());
157+
std::cerr << "Failed to execute " << cargv[0]
158+
<< ": " << strerror(errno) << std::endl;
159+
_exit(1);
160+
break;
161+
default:
162+
waitpid(child_pid, nullptr, 0);
163+
}
164+
}
165+
}

src/ek2/bootloaders/grub2.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* eclean-kernel2
2+
* (c) 2017 Michał Górny
3+
* 2-clause BSD license
4+
*/
5+
6+
#pragma once
7+
8+
#ifndef EK2_BOOTLOADERS_GRUB2_H
9+
#define EK2_BOOTLOADERS_GRUB2_H 1
10+
11+
#include "ek2/bootloader.h"
12+
#include "ek2/util/directorystream.h"
13+
#include "ek2/util/relativepath.h"
14+
15+
#include <memory>
16+
17+
class GRUB2 : public BootLoader
18+
{
19+
std::shared_ptr<DirectoryStream> grub_dir_;
20+
std::unique_ptr<RelativePath> grub_cfg_;
21+
22+
bool is_autogenerated_;
23+
bool is_multislot_;
24+
25+
bool is_installed() const;
26+
void process_config();
27+
28+
public:
29+
GRUB2(const Options& opts);
30+
static std::unique_ptr<BootLoader> construct(const Options& opts);
31+
32+
virtual void postrm();
33+
};
34+
35+
#endif /*EK2_BOOTLOADERS_GRUB2_H*/

src/main.cxx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#endif
99

1010
#include "ek2/actions.h"
11+
#include "ek2/bootloaders.h"
1112
#include "ek2/layout.h"
1213
#include "ek2/layouts.h"
1314
#include "ek2/options.h"
@@ -215,6 +216,8 @@ int sub_main(int argc, char* argv[])
215216
return 1;
216217
}
217218

219+
std::vector<std::unique_ptr<BootLoader>> bootloaders = get_bootloaders(opts);
220+
218221
l->find_kernels();
219222
std::sort(l->kernels().begin(), l->kernels().end(), f);
220223

@@ -227,7 +230,7 @@ int sub_main(int argc, char* argv[])
227230
list_kernels(*l);
228231
break;
229232
case Action::remove:
230-
remove(*l, opts);
233+
remove(*l, opts, bootloaders);
231234
break;
232235
}
233236

0 commit comments

Comments
 (0)