From c386a52079b08ad282deba0645b0ec8e2a055d26 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Tue, 8 Oct 2024 10:30:52 +0200 Subject: [PATCH 1/5] Update url pointing to rpmtag.h --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index f9195bb..1c78de9 100644 --- a/tags.go +++ b/tags.go @@ -15,7 +15,7 @@ package rpmpack // Define only tags which we actually use -// https://github.com/rpm-software-management/rpm/blob/master/lib/rpmtag.h +// https://github.com/rpm-software-management/rpm/blob/master/include/rpm/rpmtag.h const ( tagHeaderI18NTable = 0x64 // 100 // Signature tags are obiously overlapping regular header tags.. From 85cdbc2de423cafc2b75b6213f4ead70aa92e4b8 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Tue, 8 Oct 2024 10:51:30 +0200 Subject: [PATCH 2/5] Support setting the scriptlet interpreter The default interpreter '/bin/sh' can be changed via `SetDefaultScriptletInterpreter()`, or on a per-scriptlet basis via `SetScriptletInterpreterFor()` We use the more familiar name `interpreter`, RPM calls it `scriptlet program`. --- rpm.go | 172 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 51 deletions(-) diff --git a/rpm.go b/rpm.go index 51de59e..4b3c96c 100644 --- a/rpm.go +++ b/rpm.go @@ -42,7 +42,8 @@ const ( // We decided to use this approach instead of making epoch a *uint32 to // avoid a breaking change. // For reference, this is the max uint32 value, which is 4294967295. - NoEpoch = ^uint32(0) + NoEpoch = ^uint32(0) + DefaultScriptletInterpreter = "/bin/sh" ) var ( @@ -50,6 +51,15 @@ var ( ErrWriteAfterClose = errors.New("rpm write after close") // ErrWrongFileOrder is returned when files are not sorted by name. ErrWrongFileOrder = errors.New("wrong file addition order") + scriptletsTypes = map[string]scriptletType{ + "pretrans": {tag: tagPretrans, programTag: tagPretransProg}, + "prein": {tag: tagPrein, programTag: tagPreinProg}, + "postin": {tag: tagPostin, programTag: tagPostinProg}, + "preun": {tag: tagPreun, programTag: tagPreunProg}, + "postun": {tag: tagPostun, programTag: tagPostunProg}, + "posttrans": {tag: tagPosttrans, programTag: tagPosttransProg}, + "verifyscript": {tag: tagVerifyScript, programTag: tagVerifyScriptProg}, + } ) // RPMMetaData contains meta info about the whole package. @@ -101,16 +111,28 @@ type RPM struct { closed bool compressedPayload io.WriteCloser files map[string]RPMFile - prein string - postin string - preun string - postun string - pretrans string - posttrans string - verifyscript string customTags map[int]IndexEntry customSigs map[int]IndexEntry pgpSigner func([]byte) ([]byte, error) + + defaultScriptletInterpreter string + scriptlets map[string]scriptlet +} + +type scriptlet struct { + // If `interpreter` is empty, `DefaultScriptletInterpreter` is used. + // If `content` is empty and `interpreter` set, it is called without arguments. + content string + interpreter string +} + +type scriptletType struct { + tag int + programTag int +} + +type explicitScriptlets struct { + scriptlets map[string]scriptlet } // NewRPM creates and returns a new RPM struct. @@ -136,14 +158,16 @@ func NewRPM(m RPMMetaData) (*RPM, error) { m.Compressor = compressorName rpm := &RPM{ - RPMMetaData: m, - di: newDirIndex(), - payload: p, - compressedPayload: z, - cpio: cpio.NewWriter(z), - files: make(map[string]RPMFile), - customTags: make(map[int]IndexEntry), - customSigs: make(map[int]IndexEntry), + RPMMetaData: m, + di: newDirIndex(), + payload: p, + compressedPayload: z, + cpio: cpio.NewWriter(z), + files: make(map[string]RPMFile), + customTags: make(map[int]IndexEntry), + customSigs: make(map[int]IndexEntry), + defaultScriptletInterpreter: DefaultScriptletInterpreter, + scriptlets: make(map[string]scriptlet), } // A package must provide itself... @@ -273,6 +297,7 @@ func (r *RPM) Write(w io.Writer) error { // Write the regular header. h := newIndex(immutable) r.writeGenIndexes(h) + r.writeScriptlets(h, r.implicitToExplicitScriptlets()) // do not write file indexes if there are no files (meta package) // doing so will result in an invalid package @@ -426,34 +451,6 @@ func (r *RPM) writeGenIndexes(h *index) { // rpm utilities look for the sourcerpm tag to deduce if this is not a source rpm (if it has a sourcerpm, // it is NOT a source rpm). h.Add(tagSourceRPM, EntryString(fmt.Sprintf("%s-%s.src.rpm", r.Name, r.FullVersion()))) - if r.pretrans != "" { - h.Add(tagPretrans, EntryString(r.pretrans)) - h.Add(tagPretransProg, EntryString("/bin/sh")) - } - if r.prein != "" { - h.Add(tagPrein, EntryString(r.prein)) - h.Add(tagPreinProg, EntryString("/bin/sh")) - } - if r.postin != "" { - h.Add(tagPostin, EntryString(r.postin)) - h.Add(tagPostinProg, EntryString("/bin/sh")) - } - if r.preun != "" { - h.Add(tagPreun, EntryString(r.preun)) - h.Add(tagPreunProg, EntryString("/bin/sh")) - } - if r.postun != "" { - h.Add(tagPostun, EntryString(r.postun)) - h.Add(tagPostunProg, EntryString("/bin/sh")) - } - if r.posttrans != "" { - h.Add(tagPosttrans, EntryString(r.posttrans)) - h.Add(tagPosttransProg, EntryString("/bin/sh")) - } - if r.verifyscript != "" { - h.Add(tagVerifyScript, EntryString(r.verifyscript)) - h.Add(tagVerifyScriptProg, EntryString("/bin/sh")) - } } // WriteFileIndexes writes file related index headers to the header @@ -496,39 +493,112 @@ func (r *RPM) writeFileIndexes(h *index) { h.Add(tagFileLangs, EntryStringSlice(fileLangs)) } +func (r *RPM) writeScriptlets(h *index, s explicitScriptlets) { + for name, s := range s.scriptlets { + tagInfo, found := scriptletsTypes[name] + if !found { + panic(fmt.Sprintf("internal error: invalid scriptlet name %q", name)) + } + // If only an interpreter is given then the `scriptlet` becomes a `program` which is called without arguments + if s.content != "" { + h.Add(tagInfo.tag, EntryString(s.content)) + } + h.Add(tagInfo.programTag, EntryString(s.interpreter)) + } +} + +// Resolve implicit interpreter values to explicit ones +func (r *RPM) implicitToExplicitScriptlets() explicitScriptlets { + explicitScriptlets := explicitScriptlets{make(map[string]scriptlet)} + for name, s := range r.scriptlets { + if s.content == "" && s.interpreter == "" { + continue + } + explicit := scriptlet{} + _, found := scriptletsTypes[name] + if !found { + panic(fmt.Sprintf("internal error: invalid scriptlet name %q", name)) + } + explicit.content = s.content + if s.interpreter == "" { + explicit.interpreter = r.defaultScriptletInterpreter + } else { + explicit.interpreter = s.interpreter + } + explicitScriptlets.scriptlets[name] = explicit + } + return explicitScriptlets +} + +// Sets the default scriptlet interpreter (a.k.a. scriptlet program) used to call +// scriptlets, defaults to `/bin/sh`. Does not reset an interpeter previously set by +// `SetScriptletInterpreterFor()`. +// An emtpy string resets to the default. +// Note: The scriptlets of an rpm can be checked via `rpm -qp --scripts RPMFILE`. +func (r *RPM) SetDefaultScriptletInterpreter(interpreter string) { + if interpreter == "" { + r.defaultScriptletInterpreter = DefaultScriptletInterpreter + } else { + r.defaultScriptletInterpreter = interpreter + } +} + +// Set a per-scriptlet interpreter, where `name` must +// be one of: `prein postin preun postun pretrans posttrans verifyscript` +func (r *RPM) SetScriptletInterpreterFor(name, interpreter string) error { + _, found := scriptletsTypes[name] + if !found { + return fmt.Errorf("invalid scriptlet name %q", name) + } + item := r.scriptlets[name] + item.interpreter = interpreter + r.scriptlets[name] = item + return nil +} + +func (r *RPM) setScriptlet(name, content string) { + _, found := scriptletsTypes[name] + if !found { + panic(fmt.Sprintf("internal error: invalid scriptlet name %q", name)) + } + item := r.scriptlets[name] + item.content = content + r.scriptlets[name] = item +} + // AddPretrans adds a pretrans scriptlet func (r *RPM) AddPretrans(s string) { - r.pretrans = s + r.setScriptlet("pretrans", s) } // AddPrein adds a prein scriptlet func (r *RPM) AddPrein(s string) { - r.prein = s + r.setScriptlet("prein", s) } // AddPostin adds a postin scriptlet func (r *RPM) AddPostin(s string) { - r.postin = s + r.setScriptlet("postin", s) } // AddPreun adds a preun scriptlet func (r *RPM) AddPreun(s string) { - r.preun = s + r.setScriptlet("preun", s) } // AddPostun adds a postun scriptlet func (r *RPM) AddPostun(s string) { - r.postun = s + r.setScriptlet("postun", s) } // AddPosttrans adds a posttrans scriptlet func (r *RPM) AddPosttrans(s string) { - r.posttrans = s + r.setScriptlet("posttrans", s) } // AddVerifyScript adds a verifyscript scriptlet func (r *RPM) AddVerifyScript(s string) { - r.verifyscript = s + r.setScriptlet("verifyscript", s) } // AddFile adds an RPMFile to an existing rpm. From 58911cf7f87298fd6cde33dc0d43e9861424dded Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Tue, 8 Oct 2024 10:51:30 +0200 Subject: [PATCH 3/5] Support for pretrans and posttrans scriptlets These usually do not fork interpreters, so do not override them when calling `SetDefaultScriptletInterpreter()`. Note that explicity setting /bin/sh also *seems* to work, and so does setting for other scriptlet. Use at your own risk! See --- rpm.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rpm.go b/rpm.go index 4b3c96c..43035ca 100644 --- a/rpm.go +++ b/rpm.go @@ -44,6 +44,7 @@ const ( // For reference, this is the max uint32 value, which is 4294967295. NoEpoch = ^uint32(0) DefaultScriptletInterpreter = "/bin/sh" + MagicLuaMarker = "" ) var ( @@ -52,12 +53,12 @@ var ( // ErrWrongFileOrder is returned when files are not sorted by name. ErrWrongFileOrder = errors.New("wrong file addition order") scriptletsTypes = map[string]scriptletType{ - "pretrans": {tag: tagPretrans, programTag: tagPretransProg}, + "pretrans": {tag: tagPretrans, programTag: tagPretransProg, lua: true}, "prein": {tag: tagPrein, programTag: tagPreinProg}, "postin": {tag: tagPostin, programTag: tagPostinProg}, "preun": {tag: tagPreun, programTag: tagPreunProg}, "postun": {tag: tagPostun, programTag: tagPostunProg}, - "posttrans": {tag: tagPosttrans, programTag: tagPosttransProg}, + "posttrans": {tag: tagPosttrans, programTag: tagPosttransProg, lua: true}, "verifyscript": {tag: tagVerifyScript, programTag: tagVerifyScriptProg}, } ) @@ -129,6 +130,8 @@ type scriptlet struct { type scriptletType struct { tag int programTag int + // different default interpreter (pretrans, posttrans) + lua bool } type explicitScriptlets struct { @@ -515,13 +518,15 @@ func (r *RPM) implicitToExplicitScriptlets() explicitScriptlets { continue } explicit := scriptlet{} - _, found := scriptletsTypes[name] + tagInfo, found := scriptletsTypes[name] if !found { panic(fmt.Sprintf("internal error: invalid scriptlet name %q", name)) } explicit.content = s.content - if s.interpreter == "" { + if s.interpreter == "" && !tagInfo.lua { explicit.interpreter = r.defaultScriptletInterpreter + } else if s.interpreter == "" && tagInfo.lua { + explicit.interpreter = MagicLuaMarker } else { explicit.interpreter = s.interpreter } @@ -533,7 +538,8 @@ func (r *RPM) implicitToExplicitScriptlets() explicitScriptlets { // Sets the default scriptlet interpreter (a.k.a. scriptlet program) used to call // scriptlets, defaults to `/bin/sh`. Does not reset an interpeter previously set by // `SetScriptletInterpreterFor()`. -// An emtpy string resets to the default. +// An emtpy string resets to the default. pretrans and posttrans usually use and are not +// affected by this. // Note: The scriptlets of an rpm can be checked via `rpm -qp --scripts RPMFILE`. func (r *RPM) SetDefaultScriptletInterpreter(interpreter string) { if interpreter == "" { @@ -566,7 +572,7 @@ func (r *RPM) setScriptlet(name, content string) { r.scriptlets[name] = item } -// AddPretrans adds a pretrans scriptlet +// AddPretrans adds a pretrans scriptlet, usually lua code func (r *RPM) AddPretrans(s string) { r.setScriptlet("pretrans", s) } @@ -591,7 +597,7 @@ func (r *RPM) AddPostun(s string) { r.setScriptlet("postun", s) } -// AddPosttrans adds a posttrans scriptlet +// AddPosttrans adds a posttrans scriptlet, usually lua code func (r *RPM) AddPosttrans(s string) { r.setScriptlet("posttrans", s) } From 673ce66ff37ab689cea804019d2be52448634e41 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Fri, 7 Feb 2025 16:55:17 +0100 Subject: [PATCH 4/5] Add scriptlet tests --- interpreter_test.go | 225 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 interpreter_test.go diff --git a/interpreter_test.go b/interpreter_test.go new file mode 100644 index 0000000..97cc8e6 --- /dev/null +++ b/interpreter_test.go @@ -0,0 +1,225 @@ +package rpmpack + +import ( + "testing" +) + +var ( + binTest = "/bin/test" + binother = "/bin/other" + echo = "echo" + i1 = "i = 1" +) + +func EnsureSetScriptletIs( + t *testing.T, + scriptlets explicitScriptlets, + interpreter string, + expectedInterpreter string, + expectedContent string) int { + + haveInterpreter := scriptlets.scriptlets[interpreter].interpreter + haveContent := scriptlets.scriptlets[interpreter].content + + if haveInterpreter != "" { + if haveInterpreter != expectedInterpreter { + t.Errorf("%s interpreter should be %q, got %q", interpreter, expectedInterpreter, haveInterpreter) + } + if expectedContent != haveContent { + t.Errorf("%s content should be %q, got %q", interpreter, expectedContent, haveContent) + } + return 1 + } else { + return 0 + } +} +func EnsureSetInterpretersAreAll( + r *RPM, + t *testing.T, + expectedInterpreter string, + expectedContent string, + expectedTotal int) { + + scriptlets := r.implicitToExplicitScriptlets() + total := 0 + for _, interpreter := range []string{ + "prein", + "postin", + "preun", + "postun", + "verifyscript", + } { + total += EnsureSetScriptletIs(t, scriptlets, interpreter, expectedInterpreter, expectedContent) + } + + if total != expectedTotal { + t.Errorf("Saw %d interpreters, but expected %d", total, expectedTotal) + } +} + +func EnsureSetLuaInterpretersAreAll(r *RPM, t *testing.T, expectedInterpreter string, expectedContent string, expectedTotal int) { + scriptlets := r.implicitToExplicitScriptlets() + total := 0 + for _, interpreter := range []string{ + "pretrans", + "posttrans", + } { + total += EnsureSetScriptletIs(t, scriptlets, interpreter, expectedInterpreter, expectedContent) + } + if total != expectedTotal { + t.Errorf("Saw %d interpreters, but expected %d", total, expectedTotal) + } +} + +func TestDefault(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + EnsureSetInterpretersAreAll(r, t, DefaultScriptletInterpreter, "", 0) + EnsureSetLuaInterpretersAreAll(r, t, MagicLuaMarker, "", 0) +} + +func TestDefaultScriptletInterpreterWithoutContent(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.SetDefaultScriptletInterpreter(binTest) + EnsureSetInterpretersAreAll(r, t, binTest, "", 0) + EnsureSetLuaInterpretersAreAll(r, t, "", "", 0) +} + +func TestAllAdds(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.AddPretrans(i1) + r.AddPrein(echo) + r.AddPostin(echo) + r.AddPreun(echo) + r.AddPostun(echo) + r.AddPosttrans(i1) + r.AddVerifyScript(echo) + + EnsureSetInterpretersAreAll(r, t, DefaultScriptletInterpreter, echo, 5) + EnsureSetLuaInterpretersAreAll(r, t, MagicLuaMarker, i1, 2) + r.SetDefaultScriptletInterpreter(binTest) + EnsureSetInterpretersAreAll(r, t, binTest, echo, 5) +} + +func TestAllSetInterpreterFor(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.AddPretrans(i1) + r.AddPrein(echo) + r.AddPostin(echo) + r.AddPreun(echo) + r.AddPostun(echo) + r.AddPosttrans(i1) + r.AddVerifyScript(echo) + + for _, name := range []string{ + "pretrans", + "prein", + "postin", + "preun", + "postun", + "posttrans", + "verifyscript", + } { + r.SetScriptletInterpreterFor(name, binTest) + } + + EnsureSetInterpretersAreAll(r, t, binTest, echo, 5) + EnsureSetLuaInterpretersAreAll(r, t, binTest, i1, 2) +} + +func TestDefaultScriptletInterpreter(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.SetDefaultScriptletInterpreter(binTest) + r.AddPostun(echo) + r.AddPrein(echo) + r.AddPretrans(i1) + // Only set for non-lua scriptlets + EnsureSetInterpretersAreAll(r, t, binTest, echo, 2) + EnsureSetLuaInterpretersAreAll(r, t, "", i1, 1) +} + +func TestDefaultScriptletInterpreterDoesNotResetSetScriptletInterpreterFor(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.SetDefaultScriptletInterpreter(binTest) + r.AddPrein(echo) + r.SetScriptletInterpreterFor("prein", binother) + r.SetDefaultScriptletInterpreter(binTest) + r.AddPosttrans(i1) + // The SetDefaultScriptletInterpreter does not undo the more specific SetScriptletInterpreterFor + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "prein", binother, echo) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "posttrans", MagicLuaMarker, i1) +} + +func TestOverrideLuaInterpreter(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + r.AddPosttrans(i1) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "posttrans", MagicLuaMarker, i1) + r.SetScriptletInterpreterFor("posttrans", binTest) + // Explicit setting of the interpreter for a lua scriptlet: + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "posttrans", binTest, i1) + r.SetDefaultScriptletInterpreter("/foo/bar") + // But not changed again by setting the default interpreter: + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "posttrans", binTest, i1) +} + +func TestResetToDefaultInterpreter(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + // verify: change this around + r.AddVerifyScript(echo) + // prein: only modify via SetDefaultScriptletInterpreter. Check that it obeys these settings. + r.AddPrein(echo) + r.SetDefaultScriptletInterpreter(binTest) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "verifyscript", binTest, echo) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "prein", binTest, echo) + r.SetDefaultScriptletInterpreter("") + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "verifyscript", DefaultScriptletInterpreter, echo) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "prein", DefaultScriptletInterpreter, echo) + r.SetScriptletInterpreterFor("verifyscript", binTest) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "verifyscript", binTest, echo) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "prein", DefaultScriptletInterpreter, echo) + r.SetDefaultScriptletInterpreter("") + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "verifyscript", binTest, echo) + EnsureSetScriptletIs(t, r.implicitToExplicitScriptlets(), "prein", DefaultScriptletInterpreter, echo) +} + +func TestInvalidInterpreter(t *testing.T) { + r, err := NewRPM(RPMMetaData{}) + if err != nil { + t.Fatalf("NewRPM returned error %v", err) + } + + if err := r.SetScriptletInterpreterFor("mistake", binTest); err == nil { + t.Fatalf("SetScriptletInterpreterFor with invalid name should return an error") + } +} From a2cd292e93a4922b877e86e1410702c8b262312b Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Tue, 8 Oct 2024 10:58:19 +0200 Subject: [PATCH 5/5] tar2rpm: scriptlet interpreter and prein postin support Overriding the interpreter (defaults to /bin/sh) for all or a specific scriptlet is possible via `-interpreter` or `-interpreter_for NAME:INTERPRETER` where NAME is the type of scriptlet, one of: prein postin preun postun pretrans posttrans verifyscript, e.g. `prein:/bin/ksh` pretrans and posttrans, which should be lua code, are now handled. Their interpreter '' is not overwritten, unless -interpreter_for is used. Also added verifyscript support. When calling this during `rpm -V pkgname` only the stderr output will be printed. The scriptlets of an rpm can be checked via `rpm -qp --scripts RPMFILE`. --- cmd/tar2rpm/main.go | 46 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/cmd/tar2rpm/main.go b/cmd/tar2rpm/main.go index e24a120..e730b11 100644 --- a/cmd/tar2rpm/main.go +++ b/cmd/tar2rpm/main.go @@ -33,6 +33,17 @@ const ( DashStdinStdout = "-" ) +type argSlice []string + +func (s *argSlice) String() string { + return fmt.Sprintf("%v", *s) +} + +func (s *argSlice) Set(value string) error { + *s = append(*s, value) + return nil +} + var ( provides, obsoletes, @@ -57,10 +68,16 @@ var ( url = flag.String("url", "", "the rpm url") licence = flag.String("licence", "", "the rpm licence name") - prein = flag.String("prein", "", "prein scriptlet contents (not filename)") - postin = flag.String("postin", "", "postin scriptlet contents (not filename)") - preun = flag.String("preun", "", "preun scriptlet contents (not filename)") - postun = flag.String("postun", "", "postun scriptlet contents (not filename)") + prein = flag.String("prein", "", "prein scriptlet contents (not filename).") + postin = flag.String("postin", "", "postin scriptlet contents (not filename).") + preun = flag.String("preun", "", "preun scriptlet contents (not filename)") + postun = flag.String("postun", "", "postun scriptlet contents (not filename)") + pretrans = flag.String("pretrans", "", "pretrans scriptlet contents (not filename, lua code [+])") + posttrans = flag.String("posttrans", "", "posttrans scriptlet contents (not filename, lua code [+])") + verify = flag.String("verifyscript", "", "verifyscript scriptlet contents (not filename)") + + interpreter = flag.String("interpreter", rpmpack.DefaultScriptletInterpreter, "interpreter (scriptlet program) to run scriptlets with") + interpreterFor argSlice useDirAllowlist = flag.Bool("use_dir_allowlist", false, "Only include dirs in the explicit allow list") dirAllowlistFile = flag.String("dir_allowlist_file", "", "A file with one directory per line to include from the tar to the rpm") @@ -86,6 +103,9 @@ func main() { flag.Var(&recommends, "recommends", "rpm recommends values, can be just name or in the form of name=version (eg. bla=1.2.3)") flag.Var(&requires, "requires", "rpm requires values, can be just name or in the form of name=version (eg. bla=1.2.3)") flag.Var(&conflicts, "conflicts", "rpm provides values, can be just name or in the form of name=version (eg. bla=1.2.3)") + flag.Var(&interpreterFor, "interpreter_for", "override interpreter for a scriptlet, format `NAME:INTERPRETER`, where NAME is one of\n"+ + "prein postin preun postun verifyscript, e.g. prein:/bin/bash\n"+ + "[+]: pretrans and posttrans use '' by default, which is not changed by -interpreter") flag.Usage = usage flag.Parse() if *name == "" || *version == "" { @@ -137,7 +157,7 @@ func main() { defer f.Close() w = f } else { - // Only print notice if no explicit '-' is given, merge with tar notice: + // Only print notice if no explicit '-' is given, merge with tar notice: if noticeStdinStdout != "" { noticeStdinStdout += ", " } @@ -178,6 +198,19 @@ func main() { fmt.Fprintf(os.Stderr, "tar2rpm error: %v\n", err) os.Exit(1) } + + r.SetDefaultScriptletInterpreter(*interpreter) + for _, arg := range interpreterFor { + parts := strings.SplitN(arg, ":", 2) + if len(parts) != 2 { + fmt.Fprintf(os.Stderr, "invalid -interpreter_for argument %q: must contain ':'\n", arg) + os.Exit(1) + } + if err := r.SetScriptletInterpreterFor(parts[0], parts[1]); err != nil { + fmt.Fprintf(os.Stderr, "invalid -interpreter_for argument %q: %v\n", arg, err) + os.Exit(1) + } + } if *useDirAllowlist { al := map[string]bool{} if *dirAllowlistFile != "" { @@ -195,10 +228,13 @@ func main() { r.AllowListDirs(al) } + r.AddPretrans(*pretrans) r.AddPrein(*prein) r.AddPostin(*postin) r.AddPreun(*preun) r.AddPostun(*postun) + r.AddPosttrans(*posttrans) + r.AddVerifyScript(*verify) if err := r.Write(w); err != nil { fmt.Fprintf(os.Stderr, "rpm write error: %v\n", err)