1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <syslog_client.h>
26 #include <printsize.h>
27 
28 #include <cf3.defs.h>
29 
30 /*
31  * Set by cf-agent/cf-serverd from body agent/server control.
32  */
33 static char SYSLOG_HOST[MAXHOSTNAMELEN] = "localhost";
34 /*
35  * Set by cf-agent/cf-serverd from body agent/server control.
36  */
37 static uint16_t SYSLOG_PORT = 514;
38 /*
39  * Set by cf-agent/cf-serverd/cf-execd from body agent/exec/server control.
40  */
41 static int SYSLOG_FACILITY = LOG_USER;
42 
43 
SetSyslogFacility(int facility)44 void SetSyslogFacility(int facility)
45 {
46     SYSLOG_FACILITY = facility;
47 }
48 
GetSyslogFacility()49 int GetSyslogFacility()
50 {
51     return SYSLOG_FACILITY;
52 }
53 
SetSyslogHost(const char * host)54 bool SetSyslogHost(const char *host)
55 {
56     if (strlen(host) < sizeof(SYSLOG_HOST))
57     {
58         strcpy(SYSLOG_HOST, host);
59         return true;
60     }
61     else
62     {
63         return false;
64     }
65 }
66 
SetSyslogPort(uint16_t port)67 void SetSyslogPort(uint16_t port)
68 {
69     SYSLOG_PORT = port;
70 }
71 
RemoteSysLog(int log_priority,const char * log_string)72 void RemoteSysLog(int log_priority, const char *log_string)
73 {
74     time_t now = time(NULL);
75 
76     struct addrinfo query = { 0 }, *response = NULL;
77     char strport[PRINTSIZE(unsigned)];
78     xsnprintf(strport, sizeof(strport), "%u", (unsigned) SYSLOG_PORT);
79 
80     query.ai_family = AF_UNSPEC;
81     query.ai_socktype = SOCK_DGRAM;
82 
83     int err = getaddrinfo(SYSLOG_HOST, strport, &query, &response);
84     if (err != 0)
85     {
86         Log(LOG_LEVEL_INFO,
87             "Unable to find syslog_host or service: (%s/%s) %s",
88             SYSLOG_HOST, strport, gai_strerror(err));
89         if (response != NULL)
90         {
91             freeaddrinfo(response);
92         }
93         return;
94     }
95 
96     for (const struct addrinfo *ap = response; ap != NULL; ap = ap->ai_next)
97     {
98         /* No DNS lookup, just convert IP address to string. */
99         char txtaddr[CF_MAX_IP_LEN] = "";
100         getnameinfo(ap->ai_addr, ap->ai_addrlen,
101                     txtaddr, sizeof(txtaddr),
102                     NULL, 0, NI_NUMERICHOST);
103         Log(LOG_LEVEL_VERBOSE,
104             "Connect to syslog '%s' = '%s' on port '%s'",
105             SYSLOG_HOST, txtaddr, strport);
106 
107         int sd = socket(ap->ai_family, ap->ai_socktype, IPPROTO_UDP);
108         if (sd == -1)
109         {
110             Log(LOG_LEVEL_INFO, "Couldn't open a socket. (socket: %s)", GetErrorStr());
111             continue;
112         }
113         else
114         {
115             const size_t rfc3164_len = 1024;
116             char message[rfc3164_len];
117             char timebuffer[26];
118             pid_t pid = getpid();
119 
120             snprintf(
121                 message,
122                 sizeof(message),
123                 "<%i>%.15s %s %s[%ld]: %s",
124                 (log_priority | SYSLOG_FACILITY),
125                 (cf_strtimestamp_local(now, timebuffer) + 4), // Skips day str
126                 VFQNAME,
127                 VPREFIX,
128                 (long) pid,
129                 log_string);
130             err = sendto(sd, message, strlen(message),
131                          0, ap->ai_addr, ap->ai_addrlen);
132             if (err == -1)
133             {
134                 Log(LOG_LEVEL_VERBOSE, "Couldn't send '%s' to syslog server '%s'. (sendto: %s)",
135                     message, SYSLOG_HOST, GetErrorStr());
136             }
137             else
138             {
139                 Log(LOG_LEVEL_VERBOSE, "Syslog message: '%s' to server '%s'", message, SYSLOG_HOST);
140             }
141             close(sd);
142         }
143     }
144 
145     freeaddrinfo(response);
146 }
147