Compare commits
No commits in common. "master" and "set-syslog-fmt" have entirely different histories.
master
...
set-syslog
12 changed files with 55 additions and 207 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
.*.vim
|
|
||||||
/build
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "vendor/gopkg.in/mcuadros/go-syslog.v2"]
|
|
||||||
path = vendor/gopkg.in/mcuadros/go-syslog.v2
|
|
||||||
url = https://github.com/boyska/go-syslog
|
|
|
@ -54,8 +54,8 @@ func (b *BoolAuto) Set(s string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
queryAddr := flag.String("addr", "", "http service address")
|
addr := flag.String("addr", "localhost:9080", "http service address")
|
||||||
querySocket := flag.String("socket", "/tmp/circologd-query.sock", "Path to a unix domain socket for the HTTP server")
|
querySocket := flag.String("socket", "", "Path to a unix domain socket for the HTTP server")
|
||||||
backlogLimit := flag.Int("n", -1, "Limit the backlog length, defaults to no limit (-1)")
|
backlogLimit := flag.Int("n", -1, "Limit the backlog length, defaults to no limit (-1)")
|
||||||
var format formatter.Format
|
var format formatter.Format
|
||||||
format = formatter.FormatSyslog
|
format = formatter.FormatSyslog
|
||||||
|
@ -75,7 +75,7 @@ func main() {
|
||||||
signal.Notify(interrupt, os.Interrupt)
|
signal.Notify(interrupt, os.Interrupt)
|
||||||
var d *websocket.Dialer
|
var d *websocket.Dialer
|
||||||
u := url.URL{Scheme: "ws",
|
u := url.URL{Scheme: "ws",
|
||||||
Host: *queryAddr, // ignored in case of -socket; see the Dialer below
|
Host: *addr, // ignored in case of -socket; see the Dialer below
|
||||||
Path: "/ws",
|
Path: "/ws",
|
||||||
}
|
}
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
@ -84,7 +84,7 @@ func main() {
|
||||||
q.Set("l", strconv.Itoa(*backlogLimit))
|
q.Set("l", strconv.Itoa(*backlogLimit))
|
||||||
}
|
}
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
if *queryAddr == "" {
|
if *querySocket != "" {
|
||||||
d = &websocket.Dialer{
|
d = &websocket.Dialer{
|
||||||
NetDial: func(network, addr string) (net.Conn, error) {
|
NetDial: func(network, addr string) (net.Conn, error) {
|
||||||
return net.Dial("unix", *querySocket)
|
return net.Dial("unix", *querySocket)
|
||||||
|
@ -95,7 +95,7 @@ func main() {
|
||||||
log.Printf("connecting to %s", *querySocket)
|
log.Printf("connecting to %s", *querySocket)
|
||||||
} else {
|
} else {
|
||||||
d = websocket.DefaultDialer
|
d = websocket.DefaultDialer
|
||||||
log.Printf("connecting to %s", *queryAddr)
|
log.Printf("connecting to %s", *addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, _, err := d.Dial(u.String(), nil)
|
c, _, err := d.Dial(u.String(), nil)
|
||||||
|
@ -149,7 +149,7 @@ func main() {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
log.Println("Successfully close")
|
log.Println("Successfully close")
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(1 * time.Second):
|
||||||
log.Println("Forced close")
|
log.Println("Forced close")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/coreos/go-systemd/activation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Listeners() ([]net.Listener, error) {
|
|
||||||
files := activation.Files(false)
|
|
||||||
listeners := make([]net.Listener, len(files))
|
|
||||||
|
|
||||||
for i, f := range files {
|
|
||||||
if pc, err := net.FileListener(f); err == nil {
|
|
||||||
listeners[i] = pc
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return listeners, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PacketConns returns a slice containing a net.PacketConn for each matching socket type
|
|
||||||
// passed to this process.
|
|
||||||
//
|
|
||||||
// The order of the file descriptors is preserved in the returned slice.
|
|
||||||
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
|
|
||||||
// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
|
|
||||||
func PacketConns() ([]net.PacketConn, error) {
|
|
||||||
files := activation.Files(false)
|
|
||||||
conns := make([]net.PacketConn, len(files))
|
|
||||||
|
|
||||||
for i, f := range files {
|
|
||||||
if pc, err := net.FilePacketConn(f); err == nil {
|
|
||||||
conns[i] = pc
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conns, nil
|
|
||||||
}
|
|
|
@ -153,15 +153,6 @@ func getWSHandler(hub circolog.Hub) http.HandlerFunc {
|
||||||
hub.Unregister <- c
|
hub.Unregister <- c
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}()
|
}()
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
_, _, err := conn.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case message, ok := <-c.Messages:
|
case message, ok := <-c.Messages:
|
||||||
|
|
|
@ -17,29 +17,22 @@ import (
|
||||||
syslog "gopkg.in/mcuadros/go-syslog.v2"
|
syslog "gopkg.in/mcuadros/go-syslog.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var socketsToRemove []string
|
|
||||||
|
|
||||||
func cleanSocket(socket string) {
|
func cleanSocket(socket string) {
|
||||||
if err := os.Remove(socket); err != nil {
|
if err := os.Remove(socket); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error cleaning", socket, ":", err)
|
fmt.Fprintln(os.Stderr, "Error cleaning", socket, ":", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAtExit(socket string) {
|
|
||||||
socketsToRemove = append(socketsToRemove, socket)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
var syslogSocket SyslogSocket
|
|
||||||
var logFmt formatter.SyslogRFC
|
var logFmt formatter.SyslogRFC
|
||||||
logFmt.Format = syslog.Automatic
|
logFmt = formatter.Auto
|
||||||
flag.Var(&syslogSocket, "syslogd-socket", "The socket to listen to syslog addresses")
|
syslogSocketPath := flag.String("syslogd-socket", "", "The socket to listen to syslog addresses")
|
||||||
// dumpSocketPath := flag.String("dump-socket", "/run/buffer.sock", "The socket that user will connect to in order to receive logs")
|
// dumpSocketPath := flag.String("dump-socket", "/run/buffer.sock", "The socket that user will connect to in order to receive logs")
|
||||||
bufsize := flag.Int("buffer-size", 1000, "Number of messages to keep")
|
bufsize := flag.Int("buffer-size", 1000, "Number of messages to keep")
|
||||||
syslogAddr := flag.String("syslog-addr", "127.0.0.1:9514", "Address:port where to listen for syslog messages")
|
syslogAddr := flag.String("syslog-addr", "127.0.0.1:9514", "Address:port where to listen for syslog messages")
|
||||||
queryAddr := flag.String("query-addr", "", "Address:port where to bind the query service")
|
queryAddr := flag.String("query-addr", "127.0.0.1:9080", "Address:port where to bind the query service")
|
||||||
querySocket := flag.String("query-socket", "/tmp/circologd-query.sock", "Path to a unix domain socket for the HTTP server; recommended for security reasons!")
|
querySocket := flag.String("query-socket", "", "Path to a unix domain socket for the HTTP server; recommended for security reasons!")
|
||||||
ctlSocket := flag.String("ctl-socket", "/tmp/circologd-ctl.sock", "Path to a unix domain socket for the control server; leave empty to disable")
|
ctlSocket := flag.String("ctl-socket", "/tmp/circologd-ctl.sock", "Path to a unix domain socket for the control server; leave empty to disable")
|
||||||
flag.Var(&logFmt, "log-fmt", "Log messages format. If not set, defaults to automatic choice. Allowed values: rfc3164, rfc5424, auto.")
|
flag.Var(&logFmt, "log-fmt", "Log messages format. If not set, defaults to automatic choice. Allowed values: rfc3164, rfc5424, auto.")
|
||||||
verbose := flag.Bool("verbose", false, "Print more output executing the daemon")
|
verbose := flag.Bool("verbose", false, "Print more output executing the daemon")
|
||||||
|
@ -54,33 +47,20 @@ func main() {
|
||||||
go hub.Run()
|
go hub.Run()
|
||||||
|
|
||||||
server := syslog.NewServer()
|
server := syslog.NewServer()
|
||||||
server.SetFormat(logFmt.Format)
|
formatter.SetSyslogFormat(server, logFmt)
|
||||||
fmt.Printf("Syslog format set to: %s\n", logFmt.String())
|
|
||||||
server.SetHandler(handler)
|
server.SetHandler(handler)
|
||||||
if syslogSocket.isSocketActivated {
|
if *syslogSocketPath != "" {
|
||||||
fmt.Printf("Binding to socket `%s` [syslog]\n", syslogSocket.String())
|
if err = server.ListenUnixgram(*syslogSocketPath); err != nil {
|
||||||
if syslogSocket.Listener != nil {
|
fmt.Fprintln(os.Stderr, "argh", err)
|
||||||
fmt.Println("(stream)")
|
os.Exit(1)
|
||||||
server.Listen(syslogSocket.Listener)
|
|
||||||
} else {
|
|
||||||
fmt.Println("(datagram)", syslogSocket.Conn)
|
|
||||||
server.ListenDgram(syslogSocket.Conn)
|
|
||||||
}
|
}
|
||||||
|
defer cleanSocket(*syslogSocketPath)
|
||||||
|
fmt.Printf("Binding socket `%s` [syslog]\n", *syslogSocketPath)
|
||||||
} else {
|
} else {
|
||||||
syslogSocketPath := syslogSocket.Path
|
fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr)
|
||||||
if syslogSocketPath != "" {
|
if err = server.ListenUDP(*syslogAddr); err != nil {
|
||||||
if err = server.ListenUnixgram(syslogSocketPath); err != nil {
|
fmt.Fprintln(os.Stderr, "argh", err)
|
||||||
fmt.Fprintln(os.Stderr, "argh", err)
|
os.Exit(1)
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Printf("Binding socket `%s` [syslog]\n", syslogSocketPath)
|
|
||||||
removeAtExit(syslogSocketPath)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr)
|
|
||||||
if err = server.ListenUDP(*syslogAddr); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "argh", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = server.Boot(); err != nil {
|
if err = server.Boot(); err != nil {
|
||||||
|
@ -89,14 +69,14 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
httpQueryServer := http.Server{Handler: setupHTTP(hub)}
|
httpQueryServer := http.Server{Handler: setupHTTP(hub)}
|
||||||
if *queryAddr == "" {
|
if *querySocket != "" {
|
||||||
fmt.Printf("Binding address `%s` [http]\n", *querySocket)
|
fmt.Printf("Binding address `%s` [http]\n", *querySocket)
|
||||||
unixListener, err := net.Listen("unix", *querySocket)
|
unixListener, err := net.Listen("unix", *querySocket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
|
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
removeAtExit(*querySocket)
|
defer cleanSocket(*querySocket)
|
||||||
go func() {
|
go func() {
|
||||||
if err := httpQueryServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
|
if err := httpQueryServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
|
||||||
fmt.Fprintln(os.Stderr, "error binding", *querySocket, ":", err)
|
fmt.Fprintln(os.Stderr, "error binding", *querySocket, ":", err)
|
||||||
|
@ -121,7 +101,7 @@ func main() {
|
||||||
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
|
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
removeAtExit(*ctlSocket)
|
defer cleanSocket(*ctlSocket)
|
||||||
go func() {
|
go func() {
|
||||||
if err := httpCtlServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
|
if err := httpCtlServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
|
||||||
fmt.Fprintln(os.Stderr, "error binding:", err)
|
fmt.Fprintln(os.Stderr, "error binding:", err)
|
||||||
|
@ -171,9 +151,6 @@ func main() {
|
||||||
if err := httpCtlServer.Shutdown(nil); err != nil {
|
if err := httpCtlServer.Shutdown(nil); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error closing control server:", err)
|
fmt.Fprintln(os.Stderr, "Error closing control server:", err)
|
||||||
}
|
}
|
||||||
for _, socket := range socketsToRemove {
|
|
||||||
cleanSocket(socket)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyslogSocket is a struct eventually containing a net.Listener
|
|
||||||
// ready with messages, and a Path in case the Listener is not present.
|
|
||||||
type SyslogSocket struct {
|
|
||||||
Listener net.Listener
|
|
||||||
Conn net.PacketConn
|
|
||||||
Path string
|
|
||||||
isSocketActivated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set from command-line
|
|
||||||
func (s *SyslogSocket) Set(v string) error {
|
|
||||||
err := s.getActivationSocket()
|
|
||||||
if err == nil && (s.Conn != nil || s.Listener != nil) {
|
|
||||||
s.isSocketActivated = true
|
|
||||||
}
|
|
||||||
if !s.isSocketActivated {
|
|
||||||
s.Path = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyslogSocket) String() string {
|
|
||||||
if s.isSocketActivated {
|
|
||||||
return "systemd-provided"
|
|
||||||
}
|
|
||||||
return s.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyslogSocket) getActivationSocket() error {
|
|
||||||
conns, err := PacketConns()
|
|
||||||
if err == nil && len(conns) > 0 && conns[0] != nil {
|
|
||||||
s.Conn = conns[0]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
listeners, err := Listeners()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(listeners) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.Listener = listeners[0]
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -69,26 +69,4 @@ Here is a working unit for this:
|
||||||
### journald with socket activation
|
### journald with socket activation
|
||||||
|
|
||||||
To run circologd as non-root, while listening on a root-owned socket (`/run/systemd/journal/syslog`) use
|
To run circologd as non-root, while listening on a root-owned socket (`/run/systemd/journal/syslog`) use
|
||||||
socket activation. Create a unit in `/etc/systemd/system/circolog.service`:
|
socket activation
|
||||||
|
|
||||||
[Unit]
|
|
||||||
Description=In-memory logging
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
User=nobody
|
|
||||||
Group=nogroup
|
|
||||||
ExecStart=/usr/local/sbin/circologd -syslogd-socket "" -buffer-size 2000 -query-socket /run/circolog/query.sock
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
Then symlink the `syslog.service` unit to the newly created one:
|
|
||||||
|
|
||||||
ln -sf /etc/systemd/system/circolog.service /etc/systemd/system/syslog.service
|
|
||||||
|
|
||||||
and restart the service:
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl restart syslog.service
|
|
||||||
|
|
||||||
Now circolog is activated and receives messages from `journald`.
|
|
||||||
|
|
|
@ -4,6 +4,5 @@ nav:
|
||||||
- Install: install.md
|
- Install: install.md
|
||||||
- Queries: query.md
|
- Queries: query.md
|
||||||
- Hacking: hacking.md
|
- Hacking: hacking.md
|
||||||
- Systemd: systemd.md
|
|
||||||
repo_url: https://git.lattuga.net/boyska/circolog
|
repo_url: https://git.lattuga.net/boyska/circolog
|
||||||
repo_name: 'Repository'
|
repo_name: 'Repository'
|
||||||
|
|
|
@ -2,48 +2,66 @@ package formatter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
syslog "gopkg.in/mcuadros/go-syslog.v2"
|
syslog "gopkg.in/mcuadros/go-syslog.v2"
|
||||||
"gopkg.in/mcuadros/go-syslog.v2/format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyslogRFC is the formatter that the server should use
|
type SyslogRFC string
|
||||||
type SyslogRFC struct{ format.Format }
|
|
||||||
|
// SetSyslogFormat does the job of setting the server parser to the provided RFC log format.
|
||||||
|
func SetSyslogFormat(server *syslog.Server, format SyslogRFC) {
|
||||||
|
switch {
|
||||||
|
case format == Auto:
|
||||||
|
server.SetFormat(syslog.Automatic)
|
||||||
|
case format == RFC3164:
|
||||||
|
server.SetFormat(syslog.RFC3164)
|
||||||
|
case format == RFC5424:
|
||||||
|
server.SetFormat(syslog.RFC5424)
|
||||||
|
}
|
||||||
|
log.Printf("Syslog format set to: %s\n", format)
|
||||||
|
}
|
||||||
|
|
||||||
func (rfc *SyslogRFC) Set(v string) error {
|
func (rfc *SyslogRFC) Set(v string) error {
|
||||||
newval, err := parseRFCValue(v)
|
newval, err := parseRFCValue(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rfc.Format = newval
|
*rfc = newval
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rfc *SyslogRFC) String() string {
|
func (rfc *SyslogRFC) String() string {
|
||||||
switch {
|
switch {
|
||||||
case rfc.Format == syslog.Automatic:
|
case *rfc == Auto:
|
||||||
return "auto"
|
return "auto"
|
||||||
case rfc.Format == syslog.RFC3164:
|
case *rfc == RFC3164:
|
||||||
return "rfc3164"
|
return "rfc3164"
|
||||||
case rfc.Format == syslog.RFC5424:
|
case *rfc == RFC5424:
|
||||||
return "rfc5424"
|
return "rfc5424"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRFCValue(v string) (format.Format, error) {
|
func parseRFCValue(v string) (SyslogRFC, error) {
|
||||||
switch {
|
switch {
|
||||||
case v == "rfc3164":
|
case v == "rfc3164":
|
||||||
return syslog.RFC3164, nil
|
return RFC3164, nil
|
||||||
case v == "rfc5424":
|
case v == "rfc5424":
|
||||||
return syslog.RFC5424, nil
|
return RFC5424, nil
|
||||||
case v == "auto":
|
case v == "auto":
|
||||||
return syslog.Automatic, nil
|
return Auto, nil
|
||||||
default:
|
default:
|
||||||
return nil, ErrRFCNotSupported
|
return "", ErrRFCNotSupported
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RFC3164 = "rfc3164"
|
||||||
|
RFC5424 = "rfc5424"
|
||||||
|
Auto = "auto"
|
||||||
|
)
|
||||||
|
|
||||||
// ErrRFCNotSupported is raised if the supplied rfc string is
|
// ErrRFCNotSupported is raised if the supplied rfc string is
|
||||||
// not recognized.
|
// not recognized.
|
||||||
var ErrRFCNotSupported = errors.New("RFC not known")
|
var ErrRFCNotSupported = errors.New("RFC not known")
|
||||||
|
|
20
release.sh
20
release.sh
|
@ -1,20 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -u
|
|
||||||
|
|
||||||
for goosarch in $(go tool dist list | grep -vw -e aix -e js/wasm -e plan9 -e solaris -e android -e nacl)
|
|
||||||
do
|
|
||||||
mkdir -p "build/$goosarch"
|
|
||||||
goos=$(cut -d/ -f 1 <<<$goosarch)
|
|
||||||
goarch=$(cut -d/ -f 2 <<<$goosarch)
|
|
||||||
for cmd in cmd/*; do
|
|
||||||
GOOS=${goos} GOARCH=${goarch} go build -o "build/$goos/$goarch/$(basename $cmd)" ./$cmd
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
find build/ -type f|cut -d/ -f 1-3|uniq|while read -r dir; do
|
|
||||||
find $dir/ -type f -executable | xargs sha1sum > $dir/SHA1SUMS.txt
|
|
||||||
# TODO: touch to last commit date maybe
|
|
||||||
find build -exec touch -d @1234567890 {} \;
|
|
||||||
zip -q -X -j -r "circolog-$(git describe --tags --always)-$(cut -d/ -f 2-3 <<<"$dir"|tr / -)" "$dir"
|
|
||||||
done
|
|
1
vendor/gopkg.in/mcuadros/go-syslog.v2
generated
vendored
1
vendor/gopkg.in/mcuadros/go-syslog.v2
generated
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit 166aad3f993ce4a67bf486e62d637c834c8a8fe6
|
|
Loading…
Reference in a new issue