1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22
23 #include "zbxmedia.h"
24
25 #include <termios.h>
26
write_gsm(int fd,const char * str,char * error,int max_error_len)27 static int write_gsm(int fd, const char *str, char *error, int max_error_len)
28 {
29 const char *__function_name = "write_gsm";
30 int i, wlen, len, ret = SUCCEED;
31
32 zabbix_log(LOG_LEVEL_DEBUG, "In %s() str:'%s'", __function_name, str);
33
34 len = strlen(str);
35
36 for (wlen = 0; wlen < len; wlen += i)
37 {
38 if (-1 == (i = write(fd, str + wlen, len - wlen)))
39 {
40 i = 0;
41
42 if (EAGAIN == errno)
43 continue;
44
45 zabbix_log(LOG_LEVEL_DEBUG, "error writing to GSM modem: %s", zbx_strerror(errno));
46 if (NULL != error)
47 zbx_snprintf(error, max_error_len, "error writing to GSM modem: %s", zbx_strerror(errno));
48
49 ret = FAIL;
50 break;
51 }
52 }
53
54 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
55
56 return ret;
57 }
58
check_modem_result(char * buffer,char ** ebuf,char ** sbuf,const char * expect,char * error,int max_error_len)59 static int check_modem_result(char *buffer, char **ebuf, char **sbuf, const char *expect, char *error,
60 int max_error_len)
61 {
62 const char *__function_name = "check_modem_result";
63 char rcv[0xff];
64 int i, len, ret = SUCCEED;
65
66 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
67
68 zbx_strlcpy(rcv, *sbuf, sizeof(rcv));
69
70 do
71 {
72 len = *ebuf - *sbuf;
73 for (i = 0; i < len && (*sbuf)[i] != '\n' && (*sbuf)[i] != '\r'; i++)
74 ; /* find first '\r' & '\n' */
75
76 if (i < len)
77 (*sbuf)[i++] = '\0';
78
79 ret = (NULL == strstr(*sbuf, expect)) ? FAIL : SUCCEED;
80
81 *sbuf += i;
82
83 if (*sbuf != buffer)
84 {
85 memmove(buffer, *sbuf, *ebuf - *sbuf + 1); /* +1 for '\0' */
86 *ebuf -= *sbuf - buffer;
87 *sbuf = buffer;
88 }
89 }
90 while (*sbuf < *ebuf && FAIL == ret);
91
92 if (FAIL == ret && NULL != error)
93 zbx_snprintf(error, max_error_len, "Expected [%s] received [%s]", expect, rcv);
94
95 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
96
97 return ret;
98 }
99
100 #define MAX_ATTEMPTS 3
101
read_gsm(int fd,const char * expect,char * error,int max_error_len,int timeout_sec)102 static int read_gsm(int fd, const char *expect, char *error, int max_error_len, int timeout_sec)
103 {
104 const char *__function_name = "read_gsm";
105 static char buffer[0xff], *ebuf = buffer, *sbuf = buffer;
106 fd_set fdset;
107 struct timeval tv;
108 int i, nbytes, nbytes_total, rc, ret = SUCCEED;
109
110 zabbix_log(LOG_LEVEL_DEBUG, "In %s() [%s] [%s] [%s] [%s]", __function_name, expect,
111 ebuf != buffer ? buffer : "NULL", ebuf != buffer ? ebuf : "NULL", ebuf != buffer ? sbuf : "NULL");
112
113 if ('\0' != *expect && ebuf != buffer &&
114 SUCCEED == check_modem_result(buffer, &ebuf, &sbuf, expect, error, max_error_len))
115 {
116 goto out;
117 }
118
119 /* make attempts to read until there is a printable character, which would indicate a result of the command */
120
121 for (i = 0; i < MAX_ATTEMPTS; i++)
122 {
123 tv.tv_sec = timeout_sec / MAX_ATTEMPTS;
124 tv.tv_usec = (timeout_sec % MAX_ATTEMPTS) * 1000000 / MAX_ATTEMPTS;
125
126 /* wait for response from modem */
127
128 FD_ZERO(&fdset);
129 FD_SET(fd, &fdset);
130
131 while (1)
132 {
133 rc = select(fd + 1, &fdset, NULL, NULL, &tv);
134
135 if (-1 == rc)
136 {
137 if (EINTR == errno)
138 continue;
139
140 zabbix_log(LOG_LEVEL_DEBUG, "error select() for GSM modem: %s", zbx_strerror(errno));
141
142 if (NULL != error)
143 {
144 zbx_snprintf(error, max_error_len, "error select() for GSM modem: %s",
145 zbx_strerror(errno));
146 }
147
148 ret = FAIL;
149 goto out;
150 }
151 else if (0 == rc)
152 {
153 /* timeout exceeded */
154
155 zabbix_log(LOG_LEVEL_DEBUG, "error during wait for GSM modem");
156 if (NULL != error)
157 zbx_snprintf(error, max_error_len, "error during wait for GSM modem");
158
159 goto check_result;
160 }
161 else
162 break;
163 }
164
165 /* read characters into our string buffer */
166
167 nbytes_total = 0;
168
169 while (0 < (nbytes = read(fd, ebuf, buffer + sizeof(buffer) - 1 - ebuf)))
170 {
171 ebuf += nbytes;
172 *ebuf = '\0';
173
174 nbytes_total += nbytes;
175
176 zabbix_log(LOG_LEVEL_DEBUG, "Read attempt #%d from GSM modem [%s]", i, ebuf - nbytes);
177 }
178
179 while (0 < nbytes_total)
180 {
181 if (0 == isspace(ebuf[-nbytes_total]))
182 goto check_result;
183
184 nbytes_total--;
185 }
186 }
187
188 /* nul terminate the string and see if we got an OK response */
189 check_result:
190 *ebuf = '\0';
191
192 zabbix_log(LOG_LEVEL_DEBUG, "Read from GSM modem [%s]", sbuf);
193
194 if ('\0' == *expect) /* empty */
195 {
196 sbuf = ebuf = buffer;
197 *ebuf = '\0';
198 goto out;
199 }
200
201 ret = check_modem_result(buffer, &ebuf, &sbuf, expect, error, max_error_len);
202 out:
203 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
204
205 return ret;
206 }
207
208 typedef struct
209 {
210 const char *message;
211 const char *result;
212 int timeout_sec;
213 }
214 zbx_sms_scenario;
215
send_sms(const char * device,const char * number,const char * message,char * error,int max_error_len)216 int send_sms(const char *device, const char *number, const char *message, char *error, int max_error_len)
217 {
218 const char *__function_name = "send_sms";
219 #define ZBX_AT_ESC "\x1B"
220 #define ZBX_AT_CTRL_Z "\x1A"
221
222 zbx_sms_scenario scenario[] =
223 {
224 {ZBX_AT_ESC , NULL , 0}, /* Send <ESC> */
225 {"AT+CMEE=2\r" , ""/*"OK"*/ , 5}, /* verbose error values */
226 {"ATE0\r" , "OK" , 5}, /* Turn off echo */
227 {"AT\r" , "OK" , 5}, /* Init modem */
228 {"AT+CMGF=1\r" , "OK" , 5}, /* Switch to text mode */
229 {"AT+CMGS=\"" , NULL , 0}, /* Set phone number */
230 {number , NULL , 0}, /* Write phone number */
231 {"\"\r" , "> " , 5}, /* Set phone number */
232 {message , NULL , 0}, /* Write message */
233 {ZBX_AT_CTRL_Z , "+CMGS: " , 40}, /* Send message */
234 {NULL , "OK" , 1}, /* ^Z */
235 {NULL , NULL , 0}
236 };
237
238 zbx_sms_scenario *step;
239 struct termios options, old_options;
240 int f, ret = SUCCEED;
241
242 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
243
244 if (-1 == (f = open(device, O_RDWR | O_NOCTTY | O_NDELAY)))
245 {
246 zabbix_log(LOG_LEVEL_DEBUG, "error in open(%s): %s", device, zbx_strerror(errno));
247 if (NULL != error)
248 zbx_snprintf(error, max_error_len, "error in open(%s): %s", device, zbx_strerror(errno));
249 return FAIL;
250 }
251 fcntl(f, F_SETFL, 0); /* set the status flag to 0 */
252
253 /* set ta parameters */
254 tcgetattr(f, &old_options);
255
256 memset(&options, 0, sizeof(options));
257
258 options.c_iflag = IGNCR | INLCR | ICRNL;
259 #ifdef ONOCR
260 options.c_oflag = ONOCR;
261 #endif
262 options.c_cflag = old_options.c_cflag | CRTSCTS | CS8 | CLOCAL | CREAD;
263 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
264 options.c_cc[VMIN] = 0;
265 options.c_cc[VTIME] = 1;
266
267 tcsetattr(f, TCSANOW, &options);
268
269 for (step = scenario; NULL != step->message || NULL != step->result; step++)
270 {
271 if (NULL != step->message)
272 {
273 if (message == step->message)
274 {
275 char *tmp;
276
277 tmp = zbx_strdup(NULL, message);
278 zbx_remove_chars(tmp, "\r");
279
280 ret = write_gsm(f, tmp, error, max_error_len);
281
282 zbx_free(tmp);
283 }
284 else
285 ret = write_gsm(f, step->message, error, max_error_len);
286
287 if (FAIL == ret)
288 break;
289 }
290
291 if (NULL != step->result)
292 {
293 if (FAIL == (ret = read_gsm(f, step->result, error, max_error_len, step->timeout_sec)))
294 break;
295 }
296 }
297
298 if (FAIL == ret)
299 {
300 write_gsm(f, "\r" ZBX_AT_ESC ZBX_AT_CTRL_Z, NULL, 0); /* cancel all */
301 read_gsm(f, "", NULL, 0, 0); /* clear buffer */
302 }
303
304 tcsetattr(f, TCSANOW, &old_options);
305 close(f);
306
307 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
308
309 return ret;
310 }
311