1 /* nutclient.cpp - nutclient C++ library implementation
2
3 Copyright (C) 2012 Emilien Kia <emilien.kia@gmail.com>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include "nutclient.h"
21
22 #include <sstream>
23
24 #include <errno.h>
25 #include <string.h>
26 #include <stdio.h>
27
28 /* Windows/Linux Socket compatibility layer: */
29 /* Thanks to Benjamin Roux (http://broux.developpez.com/articles/c/sockets/) */
30 #ifdef WIN32
31 # include <winsock2.h>
32 #else
33 # include <sys/types.h>
34 # include <sys/socket.h>
35 # include <netinet/in.h>
36 # include <arpa/inet.h>
37 # include <unistd.h> /* close */
38 # include <netdb.h> /* gethostbyname */
39 # include <fcntl.h>
40 # define INVALID_SOCKET -1
41 # define SOCKET_ERROR -1
42 # define closesocket(s) close(s)
43 typedef int SOCKET;
44 typedef struct sockaddr_in SOCKADDR_IN;
45 typedef struct sockaddr SOCKADDR;
46 typedef struct in_addr IN_ADDR;
47 #endif /* WIN32 */
48 /* End of Windows/Linux Socket compatibility layer: */
49
50
51 /* Include nut common utility functions or define simple ones if not */
52 #ifdef HAVE_NUTCOMMON
53 #include "common.h"
54 #else /* HAVE_NUTCOMMON */
55 #include <stdlib.h>
56 #include <string.h>
xmalloc(size_t size)57 static inline void *xmalloc(size_t size){return malloc(size);}
xcalloc(size_t number,size_t size)58 static inline void *xcalloc(size_t number, size_t size){return calloc(number, size);}
xrealloc(void * ptr,size_t size)59 static inline void *xrealloc(void *ptr, size_t size){return realloc(ptr, size);}
xstrdup(const char * string)60 static inline char *xstrdup(const char *string){return strdup(string);}
61 #endif /* HAVE_NUTCOMMON */
62
63 /* To stay in line with modern C++, we use nullptr (not numeric NULL
64 * or shim __null on some systems) which was defined after C++98.
65 * The NUT C++ interface is intended for C++11 and newer, so we
66 * quiesce these warnigns if possible.
67 * An idea might be to detect if we do build with old C++ standard versions
68 * and define a nullptr like https://stackoverflow.com/a/44517878/4715872
69 * but again - currently we do not intend to support that officially.
70 */
71 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT
72 #pragma GCC diagnostic push
73 #endif
74 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_PEDANTIC
75 #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
76 #endif
77 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT
78 #pragma GCC diagnostic ignored "-Wc++98-compat"
79 #endif
80
81 namespace nut
82 {
83
SystemException()84 SystemException::SystemException():
85 NutException(err())
86 {
87 }
88
err()89 std::string SystemException::err()
90 {
91 if(errno==0)
92 return "Undefined system error";
93 else
94 {
95 std::stringstream str;
96 str << "System error " << errno << ": " << strerror(errno);
97 return str.str();
98 }
99 }
100
101 /* Implemented out-of-line to avoid "Weak vtables" warnings and related overheads
102 * But now with clang-9 C++11 linter (though not C++17) they complain with
103 * error: definition of implicit copy constructor for 'NutException'
104 * is deprecated because it has a user-declared destructor
105 * This is fixed in header with declarations like:
106 * NutException(const NutException&) = default;
107 * and assignment operator to accompany the copy constructor, per
108 * https://lgtm.com/rules/2165180572/ like:
109 * NutException& operator=(NutException& rhs) = default;
110 */
~NutException()111 NutException::~NutException() {}
~SystemException()112 SystemException::~SystemException() {}
~IOException()113 IOException::~IOException() {}
~UnknownHostException()114 UnknownHostException::~UnknownHostException() {}
~NotConnectedException()115 NotConnectedException::~NotConnectedException() {}
~TimeoutException()116 TimeoutException::~TimeoutException() {}
117
118
119 namespace internal
120 {
121
122 /**
123 * Internal socket wrapper.
124 * Provides only client socket functions.
125 *
126 * Implemented as separate internal class to easily hide plateform specificities.
127 */
128 class Socket
129 {
130 public:
131 Socket();
132 ~Socket();
133
134 void connect(const std::string& host, int port);
135 void disconnect();
136 bool isConnected()const;
137
138 void setTimeout(long timeout);
hasTimeout() const139 bool hasTimeout()const{return _tv.tv_sec>=0;}
140
141 size_t read(void* buf, size_t sz);
142 size_t write(const void* buf, size_t sz);
143
144 std::string read();
145 void write(const std::string& str);
146
147
148 private:
149 SOCKET _sock;
150 struct timeval _tv;
151 std::string _buffer; /* Received buffer, string because data should be text only. */
152 };
153
Socket()154 Socket::Socket():
155 _sock(INVALID_SOCKET),
156 _tv()
157 {
158 _tv.tv_sec = -1;
159 _tv.tv_usec = 0;
160 }
161
~Socket()162 Socket::~Socket()
163 {
164 disconnect();
165 }
166
setTimeout(long timeout)167 void Socket::setTimeout(long timeout)
168 {
169 _tv.tv_sec = timeout;
170 }
171
connect(const std::string & host,int port)172 void Socket::connect(const std::string& host, int port)
173 {
174 int sock_fd;
175 struct addrinfo hints, *res, *ai;
176 char sport[NI_MAXSERV];
177 int v;
178 fd_set wfds;
179 int error;
180 socklen_t error_size;
181 long fd_flags;
182
183 _sock = -1;
184
185 if (host.empty()) {
186 throw nut::UnknownHostException();
187 }
188
189 snprintf(sport, sizeof(sport), "%hu", static_cast<unsigned short int>(port));
190
191 memset(&hints, 0, sizeof(hints));
192 hints.ai_family = AF_UNSPEC;
193 hints.ai_socktype = SOCK_STREAM;
194 hints.ai_protocol = IPPROTO_TCP;
195
196 while ((v = getaddrinfo(host.c_str(), sport, &hints, &res)) != 0) {
197 switch (v)
198 {
199 case EAI_AGAIN:
200 continue;
201 case EAI_NONAME:
202 throw nut::UnknownHostException();
203 case EAI_SYSTEM:
204 throw nut::SystemException();
205 case EAI_MEMORY:
206 throw nut::NutException("Out of memory");
207 default:
208 throw nut::NutException("Unknown error");
209 }
210 }
211
212 for (ai = res; ai != nullptr; ai = ai->ai_next) {
213
214 sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
215
216 if (sock_fd < 0) {
217 switch (errno)
218 {
219 case EAFNOSUPPORT:
220 case EINVAL:
221 break;
222 default:
223 throw nut::SystemException();
224 }
225 continue;
226 }
227
228 /* non blocking connect */
229 if(hasTimeout()) {
230 fd_flags = fcntl(sock_fd, F_GETFL);
231 fd_flags |= O_NONBLOCK;
232 fcntl(sock_fd, F_SETFL, fd_flags);
233 }
234
235 while ((v = ::connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) {
236 if(errno == EINPROGRESS) {
237 FD_ZERO(&wfds);
238 FD_SET(sock_fd, &wfds);
239 select(sock_fd+1, nullptr, &wfds, nullptr, hasTimeout() ? &_tv : nullptr);
240 if (FD_ISSET(sock_fd, &wfds)) {
241 error_size = sizeof(error);
242 getsockopt(sock_fd, SOL_SOCKET, SO_ERROR,
243 &error, &error_size);
244 if( error == 0) {
245 /* connect successful */
246 v = 0;
247 break;
248 }
249 errno = error;
250 }
251 else {
252 /* Timeout */
253 v = -1;
254 break;
255 }
256 }
257
258 switch (errno)
259 {
260 case EAFNOSUPPORT:
261 break;
262 case EINTR:
263 case EAGAIN:
264 continue;
265 default:
266 // ups->upserror = UPSCLI_ERR_CONNFAILURE;
267 // ups->syserrno = errno;
268 break;
269 }
270 break;
271 }
272
273 if (v < 0) {
274 close(sock_fd);
275 continue;
276 }
277
278 /* switch back to blocking operation */
279 if(hasTimeout()) {
280 fd_flags = fcntl(sock_fd, F_GETFL);
281 fd_flags &= ~O_NONBLOCK;
282 fcntl(sock_fd, F_SETFL, fd_flags);
283 }
284
285 _sock = sock_fd;
286 // ups->upserror = 0;
287 // ups->syserrno = 0;
288 break;
289 }
290
291 freeaddrinfo(res);
292
293 if (_sock < 0) {
294 throw nut::IOException("Cannot connect to host");
295 }
296
297
298 #ifdef OLD
299 struct hostent *hostinfo = nullptr;
300 SOCKADDR_IN sin = { 0 };
301 hostinfo = ::gethostbyname(host.c_str());
302 if(hostinfo == nullptr) /* Host doesnt exist */
303 {
304 throw nut::UnknownHostException();
305 }
306
307 // Create socket
308 _sock = ::socket(PF_INET, SOCK_STREAM, 0);
309 if(_sock == INVALID_SOCKET)
310 {
311 throw nut::IOException("Cannot create socket");
312 }
313
314 // Connect
315 sin.sin_addr = *(IN_ADDR *) hostinfo->h_addr;
316 sin.sin_port = htons(port);
317 sin.sin_family = AF_INET;
318 if(::connect(_sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR)
319 {
320 _sock = INVALID_SOCKET;
321 throw nut::IOException("Cannot connect to host");
322 }
323 #endif // OLD
324 }
325
disconnect()326 void Socket::disconnect()
327 {
328 if(_sock != INVALID_SOCKET)
329 {
330 ::closesocket(_sock);
331 _sock = INVALID_SOCKET;
332 }
333 _buffer.clear();
334 }
335
isConnected() const336 bool Socket::isConnected()const
337 {
338 return _sock!=INVALID_SOCKET;
339 }
340
read(void * buf,size_t sz)341 size_t Socket::read(void* buf, size_t sz)
342 {
343 if(!isConnected())
344 {
345 throw nut::NotConnectedException();
346 }
347
348 if(_tv.tv_sec>=0)
349 {
350 fd_set fds;
351 FD_ZERO(&fds);
352 FD_SET(_sock, &fds);
353 int ret = select(_sock+1, &fds, nullptr, nullptr, &_tv);
354 if (ret < 1) {
355 throw nut::TimeoutException();
356 }
357 }
358
359 ssize_t res = ::read(_sock, buf, sz);
360 if(res==-1)
361 {
362 disconnect();
363 throw nut::IOException("Error while reading on socket");
364 }
365 return static_cast<size_t>(res);
366 }
367
write(const void * buf,size_t sz)368 size_t Socket::write(const void* buf, size_t sz)
369 {
370 if(!isConnected())
371 {
372 throw nut::NotConnectedException();
373 }
374
375 if(_tv.tv_sec>=0)
376 {
377 fd_set fds;
378 FD_ZERO(&fds);
379 FD_SET(_sock, &fds);
380 int ret = select(_sock+1, nullptr, &fds, nullptr, &_tv);
381 if (ret < 1) {
382 throw nut::TimeoutException();
383 }
384 }
385
386 ssize_t res = ::write(_sock, buf, sz);
387 if(res==-1)
388 {
389 disconnect();
390 throw nut::IOException("Error while writing on socket");
391 }
392 return static_cast<size_t>(res);
393 }
394
read()395 std::string Socket::read()
396 {
397 std::string res;
398 char buff[256];
399
400 while(true)
401 {
402 // Look at already read data in _buffer
403 if(!_buffer.empty())
404 {
405 size_t idx = _buffer.find('\n');
406 if(idx!=std::string::npos)
407 {
408 res += _buffer.substr(0, idx);
409 _buffer.erase(0, idx+1);
410 return res;
411 }
412 res += _buffer;
413 }
414
415 // Read new buffer
416 size_t sz = read(&buff, 256);
417 if(sz==0)
418 {
419 disconnect();
420 throw nut::IOException("Server closed connection unexpectedly");
421 }
422 _buffer.assign(buff, sz);
423 }
424 }
425
write(const std::string & str)426 void Socket::write(const std::string& str)
427 {
428 // write(str.c_str(), str.size());
429 // write("\n", 1);
430 std::string buff = str + "\n";
431 write(buff.c_str(), buff.size());
432 }
433
434 }/* namespace internal */
435
436
437 /*
438 *
439 * Client implementation
440 *
441 */
442
443 /* Pedantic builds complain about the static variable below...
444 * It is assumed safe to ignore since it is a std::string with
445 * no complex teardown at program exit.
446 */
447 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS)
448 #pragma GCC diagnostic push
449 # ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS
450 # pragma GCC diagnostic ignored "-Wglobal-constructors"
451 # endif
452 # ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS
453 # pragma GCC diagnostic ignored "-Wexit-time-destructors"
454 # endif
455 #endif
456 const Feature Client::TRACKING = "TRACKING";
457 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS)
458 #pragma GCC diagnostic pop
459 #endif
460
Client()461 Client::Client()
462 {
463 }
464
~Client()465 Client::~Client()
466 {
467 }
468
hasDevice(const std::string & dev)469 bool Client::hasDevice(const std::string& dev)
470 {
471 std::set<std::string> devs = getDeviceNames();
472 return devs.find(dev) != devs.end();
473 }
474
getDevice(const std::string & name)475 Device Client::getDevice(const std::string& name)
476 {
477 if(hasDevice(name))
478 return Device(this, name);
479 else
480 return Device(nullptr, "");
481 }
482
getDevices()483 std::set<Device> Client::getDevices()
484 {
485 std::set<Device> res;
486
487 std::set<std::string> devs = getDeviceNames();
488 for(std::set<std::string>::iterator it=devs.begin(); it!=devs.end(); ++it)
489 {
490 res.insert(Device(this, *it));
491 }
492
493 return res;
494 }
495
hasDeviceVariable(const std::string & dev,const std::string & name)496 bool Client::hasDeviceVariable(const std::string& dev, const std::string& name)
497 {
498 std::set<std::string> names = getDeviceVariableNames(dev);
499 return names.find(name) != names.end();
500 }
501
getDeviceVariableValues(const std::string & dev)502 std::map<std::string,std::vector<std::string> > Client::getDeviceVariableValues(const std::string& dev)
503 {
504 std::map<std::string,std::vector<std::string> > res;
505
506 std::set<std::string> names = getDeviceVariableNames(dev);
507 for(std::set<std::string>::iterator it=names.begin(); it!=names.end(); ++it)
508 {
509 const std::string& name = *it;
510 res[name] = getDeviceVariableValue(dev, name);
511 }
512
513 return res;
514 }
515
getDevicesVariableValues(const std::set<std::string> & devs)516 std::map<std::string,std::map<std::string,std::vector<std::string> > > Client::getDevicesVariableValues(const std::set<std::string>& devs)
517 {
518 std::map<std::string,std::map<std::string,std::vector<std::string> > > res;
519
520 for(std::set<std::string>::const_iterator it=devs.cbegin(); it!=devs.cend(); ++it)
521 {
522 res[*it] = getDeviceVariableValues(*it);
523 }
524
525 return res;
526 }
527
hasDeviceCommand(const std::string & dev,const std::string & name)528 bool Client::hasDeviceCommand(const std::string& dev, const std::string& name)
529 {
530 std::set<std::string> names = getDeviceCommandNames(dev);
531 return names.find(name) != names.end();
532 }
533
hasFeature(const Feature & feature)534 bool Client::hasFeature(const Feature& feature)
535 {
536 try
537 {
538 // If feature is known, querying it won't throw an exception.
539 isFeatureEnabled(feature);
540 return true;
541 }
542 catch(...)
543 {
544 return false;
545 }
546 }
547
548 /*
549 *
550 * TCP Client implementation
551 *
552 */
553
TcpClient()554 TcpClient::TcpClient():
555 Client(),
556 _host("localhost"),
557 _port(3493),
558 _timeout(0),
559 _socket(new internal::Socket)
560 {
561 // Do not connect now
562 }
563
TcpClient(const std::string & host,int port)564 TcpClient::TcpClient(const std::string& host, int port):
565 Client(),
566 _timeout(0),
567 _socket(new internal::Socket)
568 {
569 connect(host, port);
570 }
571
~TcpClient()572 TcpClient::~TcpClient()
573 {
574 delete _socket;
575 }
576
connect(const std::string & host,int port)577 void TcpClient::connect(const std::string& host, int port)
578 {
579 _host = host;
580 _port = port;
581 connect();
582 }
583
connect()584 void TcpClient::connect()
585 {
586 _socket->connect(_host, _port);
587 }
588
getHost() const589 std::string TcpClient::getHost()const
590 {
591 return _host;
592 }
593
getPort() const594 int TcpClient::getPort()const
595 {
596 return _port;
597 }
598
isConnected() const599 bool TcpClient::isConnected()const
600 {
601 return _socket->isConnected();
602 }
603
disconnect()604 void TcpClient::disconnect()
605 {
606 _socket->disconnect();
607 }
608
setTimeout(long timeout)609 void TcpClient::setTimeout(long timeout)
610 {
611 _timeout = timeout;
612 }
613
getTimeout() const614 long TcpClient::getTimeout()const
615 {
616 return _timeout;
617 }
618
authenticate(const std::string & user,const std::string & passwd)619 void TcpClient::authenticate(const std::string& user, const std::string& passwd)
620 {
621 detectError(sendQuery("USERNAME " + user));
622 detectError(sendQuery("PASSWORD " + passwd));
623 }
624
logout()625 void TcpClient::logout()
626 {
627 detectError(sendQuery("LOGOUT"));
628 _socket->disconnect();
629 }
630
getDevice(const std::string & name)631 Device TcpClient::getDevice(const std::string& name)
632 {
633 try
634 {
635 get("UPSDESC", name);
636 }
637 catch(NutException& ex)
638 {
639 if(ex.str()=="UNKNOWN-UPS")
640 return Device(nullptr, "");
641 else
642 throw;
643 }
644 return Device(this, name);
645 }
646
getDeviceNames()647 std::set<std::string> TcpClient::getDeviceNames()
648 {
649 std::set<std::string> res;
650
651 std::vector<std::vector<std::string> > devs = list("UPS");
652 for(std::vector<std::vector<std::string> >::iterator it=devs.begin();
653 it!=devs.end(); ++it)
654 {
655 std::string id = (*it)[0];
656 if(!id.empty())
657 res.insert(id);
658 }
659
660 return res;
661 }
662
getDeviceDescription(const std::string & name)663 std::string TcpClient::getDeviceDescription(const std::string& name)
664 {
665 return get("UPSDESC", name)[0];
666 }
667
getDeviceVariableNames(const std::string & dev)668 std::set<std::string> TcpClient::getDeviceVariableNames(const std::string& dev)
669 {
670 std::set<std::string> set;
671
672 std::vector<std::vector<std::string> > res = list("VAR", dev);
673 for(size_t n=0; n<res.size(); ++n)
674 {
675 set.insert(res[n][0]);
676 }
677
678 return set;
679 }
680
getDeviceRWVariableNames(const std::string & dev)681 std::set<std::string> TcpClient::getDeviceRWVariableNames(const std::string& dev)
682 {
683 std::set<std::string> set;
684
685 std::vector<std::vector<std::string> > res = list("RW", dev);
686 for(size_t n=0; n<res.size(); ++n)
687 {
688 set.insert(res[n][0]);
689 }
690
691 return set;
692 }
693
getDeviceVariableDescription(const std::string & dev,const std::string & name)694 std::string TcpClient::getDeviceVariableDescription(const std::string& dev, const std::string& name)
695 {
696 return get("DESC", dev + " " + name)[0];
697 }
698
getDeviceVariableValue(const std::string & dev,const std::string & name)699 std::vector<std::string> TcpClient::getDeviceVariableValue(const std::string& dev, const std::string& name)
700 {
701 return get("VAR", dev + " " + name);
702 }
703
getDeviceVariableValues(const std::string & dev)704 std::map<std::string,std::vector<std::string> > TcpClient::getDeviceVariableValues(const std::string& dev)
705 {
706
707 std::map<std::string,std::vector<std::string> > map;
708
709 std::vector<std::vector<std::string> > res = list("VAR", dev);
710 for(size_t n=0; n<res.size(); ++n)
711 {
712 std::vector<std::string>& vals = res[n];
713 std::string var = vals[0];
714 vals.erase(vals.begin());
715 map[var] = vals;
716 }
717
718 return map;
719 }
720
getDevicesVariableValues(const std::set<std::string> & devs)721 std::map<std::string,std::map<std::string,std::vector<std::string> > > TcpClient::getDevicesVariableValues(const std::set<std::string>& devs)
722 {
723 std::map<std::string,std::map<std::string,std::vector<std::string> > > map;
724
725 if (devs.empty())
726 {
727 // This request might come from processing the empty valid
728 // response of an upsd server which was allowed to start
729 // with no device sections in its ups.conf
730 return map;
731 }
732
733 std::vector<std::string> queries;
734 for (std::set<std::string>::const_iterator it=devs.cbegin(); it!=devs.cend(); ++it)
735 {
736 queries.push_back("LIST VAR " + *it);
737 }
738 sendAsyncQueries(queries);
739
740 for (std::set<std::string>::const_iterator it=devs.cbegin(); it!=devs.cend(); ++it)
741 {
742 try
743 {
744 std::map<std::string,std::vector<std::string> > map2;
745 std::vector<std::vector<std::string> > res = parseList("VAR " + *it);
746 for (std::vector<std::vector<std::string> >::iterator it2=res.begin(); it2!=res.end(); ++it2)
747 {
748 std::vector<std::string>& vals = *it2;
749 std::string var = vals[0];
750 vals.erase(vals.begin());
751 map2[var] = vals;
752 }
753 map[*it] = map2;
754 }
755 catch (NutException&)
756 {
757 // We sent a bunch of queries, we need to process them all to clear up the backlog.
758 }
759 }
760
761 if (map.empty())
762 {
763 // We may fail on some devices, but not on ALL devices.
764 throw NutException("Invalid device");
765 }
766
767 return map;
768 }
769
setDeviceVariable(const std::string & dev,const std::string & name,const std::string & value)770 TrackingID TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)
771 {
772 std::string query = "SET VAR " + dev + " " + name + " " + escape(value);
773 return sendTrackingQuery(query);
774 }
775
setDeviceVariable(const std::string & dev,const std::string & name,const std::vector<std::string> & values)776 TrackingID TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)
777 {
778 std::string query = "SET VAR " + dev + " " + name;
779 for(size_t n=0; n<values.size(); ++n)
780 {
781 query += " " + escape(values[n]);
782 }
783 return sendTrackingQuery(query);
784 }
785
getDeviceCommandNames(const std::string & dev)786 std::set<std::string> TcpClient::getDeviceCommandNames(const std::string& dev)
787 {
788 std::set<std::string> cmds;
789
790 std::vector<std::vector<std::string> > res = list("CMD", dev);
791 for(size_t n=0; n<res.size(); ++n)
792 {
793 cmds.insert(res[n][0]);
794 }
795
796 return cmds;
797 }
798
getDeviceCommandDescription(const std::string & dev,const std::string & name)799 std::string TcpClient::getDeviceCommandDescription(const std::string& dev, const std::string& name)
800 {
801 return get("CMDDESC", dev + " " + name)[0];
802 }
803
executeDeviceCommand(const std::string & dev,const std::string & name,const std::string & param)804 TrackingID TcpClient::executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param)
805 {
806 return sendTrackingQuery("INSTCMD " + dev + " " + name + " " + param);
807 }
808
deviceLogin(const std::string & dev)809 void TcpClient::deviceLogin(const std::string& dev)
810 {
811 detectError(sendQuery("LOGIN " + dev));
812 }
813
814 /* FIXME: Protocol update needed to handle master/primary alias
815 * and probably an API bump also, to rename/alias the routine.
816 */
deviceMaster(const std::string & dev)817 void TcpClient::deviceMaster(const std::string& dev)
818 {
819 detectError(sendQuery("MASTER " + dev));
820 }
821
deviceForcedShutdown(const std::string & dev)822 void TcpClient::deviceForcedShutdown(const std::string& dev)
823 {
824 detectError(sendQuery("FSD " + dev));
825 }
826
deviceGetNumLogins(const std::string & dev)827 int TcpClient::deviceGetNumLogins(const std::string& dev)
828 {
829 std::string num = get("NUMLOGINS", dev)[0];
830 return atoi(num.c_str());
831 }
832
getTrackingResult(const TrackingID & id)833 TrackingResult TcpClient::getTrackingResult(const TrackingID& id)
834 {
835 if (id.empty())
836 {
837 return TrackingResult::SUCCESS;
838 }
839
840 std::string result = sendQuery("GET TRACKING " + id);
841
842 if (result == "PENDING")
843 {
844 return TrackingResult::PENDING;
845 }
846 else if (result == "SUCCESS")
847 {
848 return TrackingResult::SUCCESS;
849 }
850 else if (result == "ERR UNKNOWN")
851 {
852 return TrackingResult::UNKNOWN;
853 }
854 else if (result == "ERR INVALID-ARGUMENT")
855 {
856 return TrackingResult::INVALID_ARGUMENT;
857 }
858 else
859 {
860 return TrackingResult::FAILURE;
861 }
862 }
863
isFeatureEnabled(const Feature & feature)864 bool TcpClient::isFeatureEnabled(const Feature& feature)
865 {
866 std::string result = sendQuery("GET " + feature);
867 detectError(result);
868
869 if (result == "ON")
870 {
871 return true;
872 }
873 else if (result == "OFF")
874 {
875 return false;
876 }
877 else
878 {
879 throw NutException("Unknown feature result " + result);
880 }
881 }
setFeature(const Feature & feature,bool status)882 void TcpClient::setFeature(const Feature& feature, bool status)
883 {
884 std::string result = sendQuery("SET " + feature + " " + (status ? "ON" : "OFF"));
885 detectError(result);
886 }
887
get(const std::string & subcmd,const std::string & params)888 std::vector<std::string> TcpClient::get
889 (const std::string& subcmd, const std::string& params)
890 {
891 std::string req = subcmd;
892 if(!params.empty())
893 {
894 req += " " + params;
895 }
896 std::string res = sendQuery("GET " + req);
897 detectError(res);
898 if(res.substr(0, req.size()) != req)
899 {
900 throw NutException("Invalid response");
901 }
902
903 return explode(res, req.size());
904 }
905
list(const std::string & subcmd,const std::string & params)906 std::vector<std::vector<std::string> > TcpClient::list
907 (const std::string& subcmd, const std::string& params)
908 {
909 std::string req = subcmd;
910 if(!params.empty())
911 {
912 req += " " + params;
913 }
914 std::vector<std::string> query;
915 query.push_back("LIST " + req);
916 sendAsyncQueries(query);
917 return parseList(req);
918 }
919
parseList(const std::string & req)920 std::vector<std::vector<std::string> > TcpClient::parseList
921 (const std::string& req)
922 {
923 std::string res = _socket->read();
924 detectError(res);
925 if(res != ("BEGIN LIST " + req))
926 {
927 throw NutException("Invalid response");
928 }
929
930 std::vector<std::vector<std::string> > arr;
931 while(true)
932 {
933 res = _socket->read();
934 detectError(res);
935 if(res == ("END LIST " + req))
936 {
937 return arr;
938 }
939 if(res.substr(0, req.size()) == req)
940 {
941 arr.push_back(explode(res, req.size()));
942 }
943 else
944 {
945 throw NutException("Invalid response");
946 }
947 }
948 }
949
sendQuery(const std::string & req)950 std::string TcpClient::sendQuery(const std::string& req)
951 {
952 _socket->write(req);
953 return _socket->read();
954 }
955
sendAsyncQueries(const std::vector<std::string> & req)956 void TcpClient::sendAsyncQueries(const std::vector<std::string>& req)
957 {
958 for (std::vector<std::string>::const_iterator it = req.cbegin(); it != req.cend(); ++it)
959 {
960 _socket->write(*it);
961 }
962 }
963
detectError(const std::string & req)964 void TcpClient::detectError(const std::string& req)
965 {
966 if(req.substr(0,3)=="ERR")
967 {
968 throw NutException(req.substr(4));
969 }
970 }
971
explode(const std::string & str,size_t begin)972 std::vector<std::string> TcpClient::explode(const std::string& str, size_t begin)
973 {
974 std::vector<std::string> res;
975 std::string temp;
976
977 enum STATE {
978 INIT,
979 SIMPLE_STRING,
980 QUOTED_STRING,
981 SIMPLE_ESCAPE,
982 QUOTED_ESCAPE
983 } state = INIT;
984
985 for(size_t idx=begin; idx<str.size(); ++idx)
986 {
987 char c = str[idx];
988 switch(state)
989 {
990 case INIT:
991 if(c==' ' /* || c=='\t' */)
992 { /* Do nothing */ }
993 else if(c=='"')
994 {
995 state = QUOTED_STRING;
996 }
997 else if(c=='\\')
998 {
999 state = SIMPLE_ESCAPE;
1000 }
1001 /* What about bad characters ? */
1002 else
1003 {
1004 temp += c;
1005 state = SIMPLE_STRING;
1006 }
1007 break;
1008 case SIMPLE_STRING:
1009 if(c==' ' /* || c=='\t' */)
1010 {
1011 /* if(!temp.empty()) : Must not occur */
1012 res.push_back(temp);
1013 temp.clear();
1014 state = INIT;
1015 }
1016 else if(c=='\\')
1017 {
1018 state = SIMPLE_ESCAPE;
1019 }
1020 else if(c=='"')
1021 {
1022 /* if(!temp.empty()) : Must not occur */
1023 res.push_back(temp);
1024 temp.clear();
1025 state = QUOTED_STRING;
1026 }
1027 /* What about bad characters ? */
1028 else
1029 {
1030 temp += c;
1031 }
1032 break;
1033 case QUOTED_STRING:
1034 if(c=='\\')
1035 {
1036 state = QUOTED_ESCAPE;
1037 }
1038 else if(c=='"')
1039 {
1040 res.push_back(temp);
1041 temp.clear();
1042 state = INIT;
1043 }
1044 /* What about bad characters ? */
1045 else
1046 {
1047 temp += c;
1048 }
1049 break;
1050 case SIMPLE_ESCAPE:
1051 if(c=='\\' || c=='"' || c==' ' /* || c=='\t'*/)
1052 {
1053 temp += c;
1054 }
1055 else
1056 {
1057 temp += '\\' + c; // Really do this ?
1058 }
1059 state = SIMPLE_STRING;
1060 break;
1061 case QUOTED_ESCAPE:
1062 if(c=='\\' || c=='"')
1063 {
1064 temp += c;
1065 }
1066 else
1067 {
1068 temp += '\\' + c; // Really do this ?
1069 }
1070 state = QUOTED_STRING;
1071 break;
1072 }
1073 }
1074
1075 if(!temp.empty())
1076 {
1077 res.push_back(temp);
1078 }
1079
1080 return res;
1081 }
1082
escape(const std::string & str)1083 std::string TcpClient::escape(const std::string& str)
1084 {
1085 std::string res = "\"";
1086
1087 for(size_t n=0; n<str.size(); n++)
1088 {
1089 char c = str[n];
1090 if(c=='"')
1091 res += "\\\"";
1092 else if(c=='\\')
1093 res += "\\\\";
1094 else
1095 res += c;
1096 }
1097
1098 res += '"';
1099 return res;
1100 }
1101
sendTrackingQuery(const std::string & req)1102 TrackingID TcpClient::sendTrackingQuery(const std::string& req)
1103 {
1104 std::string reply = sendQuery(req);
1105 detectError(reply);
1106 std::vector<std::string> res = explode(reply);
1107
1108 if (res.size() == 1 && res[0] == "OK")
1109 {
1110 return TrackingID("");
1111 }
1112 else if (res.size() == 3 && res[0] == "OK" && res[1] == "TRACKING")
1113 {
1114 return TrackingID(res[2]);
1115 }
1116 else
1117 {
1118 throw NutException("Unknown query result");
1119 }
1120 }
1121
1122 /*
1123 *
1124 * Device implementation
1125 *
1126 */
1127
Device(Client * client,const std::string & name)1128 Device::Device(Client* client, const std::string& name):
1129 _client(client),
1130 _name(name)
1131 {
1132 }
1133
Device(const Device & dev)1134 Device::Device(const Device& dev):
1135 _client(dev._client),
1136 _name(dev._name)
1137 {
1138 }
1139
operator =(const Device & dev)1140 Device& Device::operator=(const Device& dev)
1141 {
1142 // Self assignment?
1143 if (this==&dev)
1144 return *this;
1145
1146 _client = dev._client;
1147 _name = dev._name;
1148 return *this;
1149 }
1150
~Device()1151 Device::~Device()
1152 {
1153 }
1154
getName() const1155 std::string Device::getName()const
1156 {
1157 return _name;
1158 }
1159
getClient() const1160 const Client* Device::getClient()const
1161 {
1162 return _client;
1163 }
1164
getClient()1165 Client* Device::getClient()
1166 {
1167 return _client;
1168 }
1169
isOk() const1170 bool Device::isOk()const
1171 {
1172 return _client!=nullptr && !_name.empty();
1173 }
1174
operator bool() const1175 Device::operator bool()const
1176 {
1177 return isOk();
1178 }
1179
operator !() const1180 bool Device::operator!()const
1181 {
1182 return !isOk();
1183 }
1184
operator ==(const Device & dev) const1185 bool Device::operator==(const Device& dev)const
1186 {
1187 return dev._client==_client && dev._name==_name;
1188 }
1189
operator <(const Device & dev) const1190 bool Device::operator<(const Device& dev)const
1191 {
1192 return getName()<dev.getName();
1193 }
1194
getDescription()1195 std::string Device::getDescription()
1196 {
1197 if (!isOk()) throw NutException("Invalid device");
1198 return getClient()->getDeviceDescription(getName());
1199 }
1200
getVariableValue(const std::string & name)1201 std::vector<std::string> Device::getVariableValue(const std::string& name)
1202 {
1203 if (!isOk()) throw NutException("Invalid device");
1204 return getClient()->getDeviceVariableValue(getName(), name);
1205 }
1206
getVariableValues()1207 std::map<std::string,std::vector<std::string> > Device::getVariableValues()
1208 {
1209 if (!isOk()) throw NutException("Invalid device");
1210 return getClient()->getDeviceVariableValues(getName());
1211 }
1212
getVariableNames()1213 std::set<std::string> Device::getVariableNames()
1214 {
1215 if (!isOk()) throw NutException("Invalid device");
1216 return getClient()->getDeviceVariableNames(getName());
1217 }
1218
getRWVariableNames()1219 std::set<std::string> Device::getRWVariableNames()
1220 {
1221 if (!isOk()) throw NutException("Invalid device");
1222 return getClient()->getDeviceRWVariableNames(getName());
1223 }
1224
setVariable(const std::string & name,const std::string & value)1225 void Device::setVariable(const std::string& name, const std::string& value)
1226 {
1227 if (!isOk()) throw NutException("Invalid device");
1228 getClient()->setDeviceVariable(getName(), name, value);
1229 }
1230
setVariable(const std::string & name,const std::vector<std::string> & values)1231 void Device::setVariable(const std::string& name, const std::vector<std::string>& values)
1232 {
1233 if (!isOk()) throw NutException("Invalid device");
1234 getClient()->setDeviceVariable(getName(), name, values);
1235 }
1236
1237
1238
getVariable(const std::string & name)1239 Variable Device::getVariable(const std::string& name)
1240 {
1241 if (!isOk()) throw NutException("Invalid device");
1242 if(getClient()->hasDeviceVariable(getName(), name))
1243 return Variable(this, name);
1244 else
1245 return Variable(nullptr, "");
1246 }
1247
getVariables()1248 std::set<Variable> Device::getVariables()
1249 {
1250 std::set<Variable> set;
1251 if (!isOk()) throw NutException("Invalid device");
1252
1253 std::set<std::string> names = getClient()->getDeviceVariableNames(getName());
1254 for(std::set<std::string>::iterator it=names.begin(); it!=names.end(); ++it)
1255 {
1256 set.insert(Variable(this, *it));
1257 }
1258
1259 return set;
1260 }
1261
getRWVariables()1262 std::set<Variable> Device::getRWVariables()
1263 {
1264 std::set<Variable> set;
1265 if (!isOk()) throw NutException("Invalid device");
1266
1267 std::set<std::string> names = getClient()->getDeviceRWVariableNames(getName());
1268 for(std::set<std::string>::iterator it=names.begin(); it!=names.end(); ++it)
1269 {
1270 set.insert(Variable(this, *it));
1271 }
1272
1273 return set;
1274 }
1275
getCommandNames()1276 std::set<std::string> Device::getCommandNames()
1277 {
1278 if (!isOk()) throw NutException("Invalid device");
1279 return getClient()->getDeviceCommandNames(getName());
1280 }
1281
getCommands()1282 std::set<Command> Device::getCommands()
1283 {
1284 std::set<Command> cmds;
1285
1286 std::set<std::string> res = getCommandNames();
1287 for(std::set<std::string>::iterator it=res.begin(); it!=res.end(); ++it)
1288 {
1289 cmds.insert(Command(this, *it));
1290 }
1291
1292 return cmds;
1293 }
1294
getCommand(const std::string & name)1295 Command Device::getCommand(const std::string& name)
1296 {
1297 if (!isOk()) throw NutException("Invalid device");
1298 if(getClient()->hasDeviceCommand(getName(), name))
1299 return Command(this, name);
1300 else
1301 return Command(nullptr, "");
1302 }
1303
executeCommand(const std::string & name,const std::string & param)1304 TrackingID Device::executeCommand(const std::string& name, const std::string& param)
1305 {
1306 if (!isOk()) throw NutException("Invalid device");
1307 return getClient()->executeDeviceCommand(getName(), name, param);
1308 }
1309
login()1310 void Device::login()
1311 {
1312 if (!isOk()) throw NutException("Invalid device");
1313 getClient()->deviceLogin(getName());
1314 }
1315
1316 /* FIXME: Protocol update needed to handle master/primary alias
1317 * and probably an API bump also, to rename/alias the routine.
1318 */
master()1319 void Device::master()
1320 {
1321 if (!isOk()) throw NutException("Invalid device");
1322 getClient()->deviceMaster(getName());
1323 }
1324
forcedShutdown()1325 void Device::forcedShutdown()
1326 {
1327 }
1328
getNumLogins()1329 int Device::getNumLogins()
1330 {
1331 if (!isOk()) throw NutException("Invalid device");
1332 return getClient()->deviceGetNumLogins(getName());
1333 }
1334
1335 /*
1336 *
1337 * Variable implementation
1338 *
1339 */
1340
Variable(Device * dev,const std::string & name)1341 Variable::Variable(Device* dev, const std::string& name):
1342 _device(dev),
1343 _name(name)
1344 {
1345 }
1346
Variable(const Variable & var)1347 Variable::Variable(const Variable& var):
1348 _device(var._device),
1349 _name(var._name)
1350 {
1351 }
1352
operator =(const Variable & var)1353 Variable& Variable::operator=(const Variable& var)
1354 {
1355 // Self assignment?
1356 if (this==&var)
1357 return *this;
1358
1359 _device = var._device;
1360 _name = var._name;
1361 return *this;
1362 }
1363
~Variable()1364 Variable::~Variable()
1365 {
1366 }
1367
getName() const1368 std::string Variable::getName()const
1369 {
1370 return _name;
1371 }
1372
getDevice() const1373 const Device* Variable::getDevice()const
1374 {
1375 return _device;
1376 }
1377
getDevice()1378 Device* Variable::getDevice()
1379 {
1380 return _device;
1381 }
1382
isOk() const1383 bool Variable::isOk()const
1384 {
1385 return _device!=nullptr && !_name.empty();
1386
1387 }
1388
operator bool() const1389 Variable::operator bool()const
1390 {
1391 return isOk();
1392 }
1393
operator !() const1394 bool Variable::operator!()const
1395 {
1396 return !isOk();
1397 }
1398
operator ==(const Variable & var) const1399 bool Variable::operator==(const Variable& var)const
1400 {
1401 return var._device==_device && var._name==_name;
1402 }
1403
operator <(const Variable & var) const1404 bool Variable::operator<(const Variable& var)const
1405 {
1406 return getName()<var.getName();
1407 }
1408
getValue()1409 std::vector<std::string> Variable::getValue()
1410 {
1411 return getDevice()->getClient()->getDeviceVariableValue(getDevice()->getName(), getName());
1412 }
1413
getDescription()1414 std::string Variable::getDescription()
1415 {
1416 return getDevice()->getClient()->getDeviceVariableDescription(getDevice()->getName(), getName());
1417 }
1418
setValue(const std::string & value)1419 void Variable::setValue(const std::string& value)
1420 {
1421 getDevice()->setVariable(getName(), value);
1422 }
1423
setValues(const std::vector<std::string> & values)1424 void Variable::setValues(const std::vector<std::string>& values)
1425 {
1426 getDevice()->setVariable(getName(), values);
1427 }
1428
1429
1430 /*
1431 *
1432 * Command implementation
1433 *
1434 */
1435
Command(Device * dev,const std::string & name)1436 Command::Command(Device* dev, const std::string& name):
1437 _device(dev),
1438 _name(name)
1439 {
1440 }
1441
Command(const Command & cmd)1442 Command::Command(const Command& cmd):
1443 _device(cmd._device),
1444 _name(cmd._name)
1445 {
1446 }
1447
operator =(const Command & cmd)1448 Command& Command::operator=(const Command& cmd)
1449 {
1450 // Self assignment?
1451 if (this==&cmd)
1452 return *this;
1453
1454 _device = cmd._device;
1455 _name = cmd._name;
1456 return *this;
1457 }
1458
~Command()1459 Command::~Command()
1460 {
1461 }
1462
getName() const1463 std::string Command::getName()const
1464 {
1465 return _name;
1466 }
1467
getDevice() const1468 const Device* Command::getDevice()const
1469 {
1470 return _device;
1471 }
1472
getDevice()1473 Device* Command::getDevice()
1474 {
1475 return _device;
1476 }
1477
isOk() const1478 bool Command::isOk()const
1479 {
1480 return _device!=nullptr && !_name.empty();
1481 }
1482
operator bool() const1483 Command::operator bool()const
1484 {
1485 return isOk();
1486 }
1487
operator !() const1488 bool Command::operator!()const
1489 {
1490 return !isOk();
1491 }
1492
operator ==(const Command & cmd) const1493 bool Command::operator==(const Command& cmd)const
1494 {
1495 return cmd._device==_device && cmd._name==_name;
1496 }
1497
operator <(const Command & cmd) const1498 bool Command::operator<(const Command& cmd)const
1499 {
1500 return getName()<cmd.getName();
1501 }
1502
getDescription()1503 std::string Command::getDescription()
1504 {
1505 return getDevice()->getClient()->getDeviceCommandDescription(getDevice()->getName(), getName());
1506 }
1507
execute(const std::string & param)1508 void Command::execute(const std::string& param)
1509 {
1510 getDevice()->executeCommand(getName(), param);
1511 }
1512
1513 } /* namespace nut */
1514
1515
1516 /**
1517 * C nutclient API.
1518 */
1519 extern "C" {
1520
1521
strarr_alloc(size_t count)1522 strarr strarr_alloc(size_t count)
1523 {
1524 strarr arr = static_cast<strarr>(xcalloc(count+1, sizeof(char*)));
1525
1526 if (arr == nullptr) {
1527 throw nut::NutException("Out of memory");
1528 }
1529
1530 arr[count] = nullptr;
1531 return arr;
1532 }
1533
strarr_free(strarr arr)1534 void strarr_free(strarr arr)
1535 {
1536 char** pstr = arr;
1537 while(*pstr!=nullptr)
1538 {
1539 free(*pstr);
1540 ++pstr;
1541 }
1542 free(arr);
1543 }
1544
stringset_to_strarr(const std::set<std::string> & strset)1545 strarr stringset_to_strarr(const std::set<std::string>& strset)
1546 {
1547 strarr arr = strarr_alloc(strset.size());
1548 strarr pstr = arr;
1549 for(std::set<std::string>::const_iterator it=strset.begin(); it!=strset.end(); ++it)
1550 {
1551 *pstr = xstrdup(it->c_str());
1552 pstr++;
1553 }
1554 return arr;
1555 }
1556
stringvector_to_strarr(const std::vector<std::string> & strset)1557 strarr stringvector_to_strarr(const std::vector<std::string>& strset)
1558 {
1559 strarr arr = strarr_alloc(strset.size());
1560 strarr pstr = arr;
1561 for(std::vector<std::string>::const_iterator it=strset.begin(); it!=strset.end(); ++it)
1562 {
1563 *pstr = xstrdup(it->c_str());
1564 pstr++;
1565 }
1566 return arr;
1567 }
1568
nutclient_tcp_create_client(const char * host,unsigned short port)1569 NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port)
1570 {
1571 nut::TcpClient* client = new nut::TcpClient;
1572 try
1573 {
1574 client->connect(host, port);
1575 return static_cast<NUTCLIENT_TCP_t>(client);
1576 }
1577 catch(nut::NutException& ex)
1578 {
1579 // TODO really catch it
1580 NUT_UNUSED_VARIABLE(ex);
1581 delete client;
1582 return nullptr;
1583 }
1584
1585 }
1586
nutclient_destroy(NUTCLIENT_t client)1587 void nutclient_destroy(NUTCLIENT_t client)
1588 {
1589 if(client)
1590 {
1591 delete static_cast<nut::Client*>(client);
1592 }
1593 }
1594
nutclient_tcp_is_connected(NUTCLIENT_TCP_t client)1595 int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client)
1596 {
1597 if(client)
1598 {
1599 nut::TcpClient* cl = dynamic_cast<nut::TcpClient*>(static_cast<nut::Client*>(client));
1600 if(cl)
1601 {
1602 return cl->isConnected() ? 1 : 0;
1603 }
1604 }
1605 return 0;
1606 }
1607
nutclient_tcp_disconnect(NUTCLIENT_TCP_t client)1608 void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client)
1609 {
1610 if(client)
1611 {
1612 nut::TcpClient* cl = dynamic_cast<nut::TcpClient*>(static_cast<nut::Client*>(client));
1613 if(cl)
1614 {
1615 cl->disconnect();
1616 }
1617 }
1618 }
1619
nutclient_tcp_reconnect(NUTCLIENT_TCP_t client)1620 int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client)
1621 {
1622 if(client)
1623 {
1624 nut::TcpClient* cl = dynamic_cast<nut::TcpClient*>(static_cast<nut::Client*>(client));
1625 if(cl)
1626 {
1627 try
1628 {
1629 cl->connect();
1630 return 0;
1631 }
1632 catch(...){}
1633 }
1634 }
1635 return -1;
1636 }
1637
nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client,long timeout)1638 void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout)
1639 {
1640 if(client)
1641 {
1642 nut::TcpClient* cl = dynamic_cast<nut::TcpClient*>(static_cast<nut::Client*>(client));
1643 if(cl)
1644 {
1645 cl->setTimeout(timeout);
1646 }
1647 }
1648 }
1649
nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client)1650 long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client)
1651 {
1652 if(client)
1653 {
1654 nut::TcpClient* cl = dynamic_cast<nut::TcpClient*>(static_cast<nut::Client*>(client));
1655 if(cl)
1656 {
1657 return cl->getTimeout();
1658 }
1659 }
1660 return -1;
1661 }
1662
nutclient_authenticate(NUTCLIENT_t client,const char * login,const char * passwd)1663 void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd)
1664 {
1665 if(client)
1666 {
1667 nut::Client* cl = static_cast<nut::Client*>(client);
1668 if(cl)
1669 {
1670 try
1671 {
1672 cl->authenticate(login, passwd);
1673 }
1674 catch(...){}
1675 }
1676 }
1677 }
1678
nutclient_logout(NUTCLIENT_t client)1679 void nutclient_logout(NUTCLIENT_t client)
1680 {
1681 if(client)
1682 {
1683 nut::Client* cl = static_cast<nut::Client*>(client);
1684 if(cl)
1685 {
1686 try
1687 {
1688 cl->logout();
1689 }
1690 catch(...){}
1691 }
1692 }
1693 }
1694
nutclient_device_login(NUTCLIENT_t client,const char * dev)1695 void nutclient_device_login(NUTCLIENT_t client, const char* dev)
1696 {
1697 if(client)
1698 {
1699 nut::Client* cl = static_cast<nut::Client*>(client);
1700 if(cl)
1701 {
1702 try
1703 {
1704 cl->deviceLogin(dev);
1705 }
1706 catch(...){}
1707 }
1708 }
1709 }
1710
nutclient_get_device_num_logins(NUTCLIENT_t client,const char * dev)1711 int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev)
1712 {
1713 if(client)
1714 {
1715 nut::Client* cl = static_cast<nut::Client*>(client);
1716 if(cl)
1717 {
1718 try
1719 {
1720 return cl->deviceGetNumLogins(dev);
1721 }
1722 catch(...){}
1723 }
1724 }
1725 return -1;
1726 }
1727
1728 /* FIXME: Protocol update needed to handle master/primary alias
1729 * and probably an API bump also, to rename/alias the routine.
1730 */
nutclient_device_master(NUTCLIENT_t client,const char * dev)1731 void nutclient_device_master(NUTCLIENT_t client, const char* dev)
1732 {
1733 if(client)
1734 {
1735 nut::Client* cl = static_cast<nut::Client*>(client);
1736 if(cl)
1737 {
1738 try
1739 {
1740 cl->deviceMaster(dev);
1741 }
1742 catch(...){}
1743 }
1744 }
1745 }
1746
nutclient_device_forced_shutdown(NUTCLIENT_t client,const char * dev)1747 void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev)
1748 {
1749 if(client)
1750 {
1751 nut::Client* cl = static_cast<nut::Client*>(client);
1752 if(cl)
1753 {
1754 try
1755 {
1756 cl->deviceForcedShutdown(dev);
1757 }
1758 catch(...){}
1759 }
1760 }
1761 }
1762
nutclient_get_devices(NUTCLIENT_t client)1763 strarr nutclient_get_devices(NUTCLIENT_t client)
1764 {
1765 if(client)
1766 {
1767 nut::Client* cl = static_cast<nut::Client*>(client);
1768 if(cl)
1769 {
1770 try
1771 {
1772 return stringset_to_strarr(cl->getDeviceNames());
1773 }
1774 catch(...){}
1775 }
1776 }
1777 return nullptr;
1778 }
1779
nutclient_has_device(NUTCLIENT_t client,const char * dev)1780 int nutclient_has_device(NUTCLIENT_t client, const char* dev)
1781 {
1782 if(client)
1783 {
1784 nut::Client* cl = static_cast<nut::Client*>(client);
1785 if(cl)
1786 {
1787 try
1788 {
1789 return cl->hasDevice(dev)?1:0;
1790 }
1791 catch(...){}
1792 }
1793 }
1794 return 0;
1795 }
1796
nutclient_get_device_description(NUTCLIENT_t client,const char * dev)1797 char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev)
1798 {
1799 if(client)
1800 {
1801 nut::Client* cl = static_cast<nut::Client*>(client);
1802 if(cl)
1803 {
1804 try
1805 {
1806 return xstrdup(cl->getDeviceDescription(dev).c_str());
1807 }
1808 catch(...){}
1809 }
1810 }
1811 return nullptr;
1812 }
1813
nutclient_get_device_variables(NUTCLIENT_t client,const char * dev)1814 strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev)
1815 {
1816 if(client)
1817 {
1818 nut::Client* cl = static_cast<nut::Client*>(client);
1819 if(cl)
1820 {
1821 try
1822 {
1823 return stringset_to_strarr(cl->getDeviceVariableNames(dev));
1824 }
1825 catch(...){}
1826 }
1827 }
1828 return nullptr;
1829 }
1830
nutclient_get_device_rw_variables(NUTCLIENT_t client,const char * dev)1831 strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev)
1832 {
1833 if(client)
1834 {
1835 nut::Client* cl = static_cast<nut::Client*>(client);
1836 if(cl)
1837 {
1838 try
1839 {
1840 return stringset_to_strarr(cl->getDeviceRWVariableNames(dev));
1841 }
1842 catch(...){}
1843 }
1844 }
1845 return nullptr;
1846 }
1847
nutclient_has_device_variable(NUTCLIENT_t client,const char * dev,const char * var)1848 int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var)
1849 {
1850 if(client)
1851 {
1852 nut::Client* cl = static_cast<nut::Client*>(client);
1853 if(cl)
1854 {
1855 try
1856 {
1857 return cl->hasDeviceVariable(dev, var)?1:0;
1858 }
1859 catch(...){}
1860 }
1861 }
1862 return 0;
1863 }
1864
nutclient_get_device_variable_description(NUTCLIENT_t client,const char * dev,const char * var)1865 char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var)
1866 {
1867 if(client)
1868 {
1869 nut::Client* cl = static_cast<nut::Client*>(client);
1870 if(cl)
1871 {
1872 try
1873 {
1874 return xstrdup(cl->getDeviceVariableDescription(dev, var).c_str());
1875 }
1876 catch(...){}
1877 }
1878 }
1879 return nullptr;
1880 }
1881
nutclient_get_device_variable_values(NUTCLIENT_t client,const char * dev,const char * var)1882 strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var)
1883 {
1884 if(client)
1885 {
1886 nut::Client* cl = static_cast<nut::Client*>(client);
1887 if(cl)
1888 {
1889 try
1890 {
1891 return stringvector_to_strarr(cl->getDeviceVariableValue(dev, var));
1892 }
1893 catch(...){}
1894 }
1895 }
1896 return nullptr;
1897 }
1898
nutclient_set_device_variable_value(NUTCLIENT_t client,const char * dev,const char * var,const char * value)1899 void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value)
1900 {
1901 if(client)
1902 {
1903 nut::Client* cl = static_cast<nut::Client*>(client);
1904 if(cl)
1905 {
1906 try
1907 {
1908 cl->setDeviceVariable(dev, var, value);
1909 }
1910 catch(...){}
1911 }
1912 }
1913 }
1914
nutclient_set_device_variable_values(NUTCLIENT_t client,const char * dev,const char * var,const strarr values)1915 void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values)
1916 {
1917 if(client)
1918 {
1919 nut::Client* cl = static_cast<nut::Client*>(client);
1920 if(cl)
1921 {
1922 try
1923 {
1924 std::vector<std::string> vals;
1925 strarr pstr = static_cast<strarr>(values);
1926 while(*pstr)
1927 {
1928 vals.push_back(std::string(*pstr));
1929 ++pstr;
1930 }
1931
1932 cl->setDeviceVariable(dev, var, vals);
1933 }
1934 catch(...){}
1935 }
1936 }
1937 }
1938
nutclient_get_device_commands(NUTCLIENT_t client,const char * dev)1939 strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev)
1940 {
1941 if(client)
1942 {
1943 nut::Client* cl = static_cast<nut::Client*>(client);
1944 if(cl)
1945 {
1946 try
1947 {
1948 return stringset_to_strarr(cl->getDeviceCommandNames(dev));
1949 }
1950 catch(...){}
1951 }
1952 }
1953 return nullptr;
1954 }
1955
nutclient_has_device_command(NUTCLIENT_t client,const char * dev,const char * cmd)1956 int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd)
1957 {
1958 if(client)
1959 {
1960 nut::Client* cl = static_cast<nut::Client*>(client);
1961 if(cl)
1962 {
1963 try
1964 {
1965 return cl->hasDeviceCommand(dev, cmd)?1:0;
1966 }
1967 catch(...){}
1968 }
1969 }
1970 return 0;
1971 }
1972
nutclient_get_device_command_description(NUTCLIENT_t client,const char * dev,const char * cmd)1973 char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd)
1974 {
1975 if(client)
1976 {
1977 nut::Client* cl = static_cast<nut::Client*>(client);
1978 if(cl)
1979 {
1980 try
1981 {
1982 return xstrdup(cl->getDeviceCommandDescription(dev, cmd).c_str());
1983 }
1984 catch(...){}
1985 }
1986 }
1987 return nullptr;
1988 }
1989
nutclient_execute_device_command(NUTCLIENT_t client,const char * dev,const char * cmd,const char * param)1990 void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd, const char* param)
1991 {
1992 if(client)
1993 {
1994 nut::Client* cl = static_cast<nut::Client*>(client);
1995 if(cl)
1996 {
1997 try
1998 {
1999 cl->executeDeviceCommand(dev, cmd, param);
2000 }
2001 catch(...){}
2002 }
2003 }
2004 }
2005
2006 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT
2007 #pragma GCC diagnostic pop
2008 #endif
2009
2010 } /* extern "C" */
2011