Subversion Repositories heizung

Rev

Rev 10 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 root 1
/*
11 andreas 2
 * (C) Copyright 2010 to 2015, 2024 by Andreas Theofilu <andreas@theosys.at>
1 root 3
 * All rights reserved!
4
 */
11 andreas 5
//#define SENSOR_ELV
6
#define SENSOR_ETHERNET
1 root 7
 
8
#include <stdio.h>
9
#include <string.h>
10
#include <unistd.h>
11
#include <stdlib.h>
9 andreas 12
#include <libgen.h>
1 root 13
#include <math.h>
14
#include <time.h>
15
#include <signal.h>
16
#include <syslog.h>
17
#include <errno.h>
18
#include <pthread.h>
19
#include <sys/stat.h>
20
#include <sys/types.h>
21
#include <sys/socket.h>
11 andreas 22
#include <poll.h>
1 root 23
#include <fcntl.h>
24
#include <netdb.h>
9 andreas 25
#include <dirent.h>
1 root 26
#include <netinet/in.h>
11 andreas 27
#include <arpa/inet.h>
1 root 28
#include <sys/param.h>
29
#include <sys/ioctl.h>
11 andreas 30
#include <sys/time.h>
1 root 31
#include <pwd.h>
32
#include <grp.h>
5 andreas 33
#include "sensor.h"
34
#include "usb_comm.h"
35
#include "heizung.h"
1 root 36
 
11 andreas 37
#ifndef ulong
38
#define ulong       unsigned long
39
#endif
40
 
1 root 41
typedef struct
42
{
11 andreas 43
    int wday;
44
    ulong start;
45
    ulong end;
46
    float temp;
47
} HEIZUNG;
1 root 48
 
49
typedef struct HEIZINDEX
50
{
11 andreas 51
    HEIZUNG *heizung;
52
    struct HEIZINDEX *next;
53
} HEIZINDEX;
1 root 54
 
9 andreas 55
struct SOCKETS
56
{
11 andreas 57
    int sockfd;
58
    int newfd;
59
    int valid;
9 andreas 60
};
61
 
1 root 62
CONFIGURE configs;
63
HEIZINDEX *HeizFirst;
64
float ActTemperature;
65
float ActPressure;
11 andreas 66
struct SOCKETS soc;
67
int killed = 0;
1 root 68
 
11 andreas 69
static pthread_t pthr_pars, pthr_temp_pars, pthr_process, pthr_run_change;
1 root 70
 
71
// Prototypes
72
void daemon_start(int ignsigcld);
5 andreas 73
void *pthr_temperature(void *pV_data);
1 root 74
void *pthr_parser(void *pV_data);
11 andreas 75
void sig_child(int);
9 andreas 76
void sendList(int s1);
77
int listSchemas(int s1);
1 root 78
int parseCommand(int s1, char *cmd);
9 andreas 79
void *processCommands(void *pV_data);
4 andreas 80
void changeToUser(char *, char *);
11 andreas 81
void *pthr_report(void *pV_data);
1 root 82
 
83
HEIZINDEX *allocateMemory(void);
4 andreas 84
HEIZINDEX *insertMemory(HEIZINDEX *pos);
1 root 85
void freeMemory(void);
4 andreas 86
HEIZINDEX *freeChainMember(HEIZINDEX *member);
87
void freeDay(int wday);
88
void insertMember(int wday, long endt, float temp);
9 andreas 89
char *makeFileName(char *ret, char *fname, int len);
1 root 90
int readHeizPlan(void);
9 andreas 91
int readHeizPlanName(char *fname);
1 root 92
int writeHeizPlan(void);
9 andreas 93
int writeHeizPlanName(char *fname);
1 root 94
void readConf(void);
95
 
10 andreas 96
HEIZINDEX *allocMem(HEIZINDEX *first);
97
int sortTable(void);
98
 
4 andreas 99
char *readLine(int fd, char *buf, int bufLen);
1 root 100
char *trim(char *str);
2 andreas 101
char *remove_string(char *str, char *search, char *ret);
1 root 102
 
10 andreas 103
//static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
11 andreas 104
//static pthread_mutex_t fastmutex_ser = PTHREAD_MUTEX_INITIALIZER;
105
//static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;
1 root 106
 
107
/*
108
 * The main program, initializing everything and start the daemon.
109
 */
110
int main(int argc, char *argv[])
111
{
11 andreas 112
    HeizFirst = NULL;
113
    memset(&soc, 0, sizeof(soc));
1 root 114
 
11 andreas 115
    readConf();
116
    // Initialize USB
117
    serialDev.fd = -1;
118
    serialDev.switch_fd = -1;
119
    serialDev.handle = NULL;
120
    // Now daemonize this application
121
    daemon_start(0);
122
    changeToUser(&configs.User[0], &configs.Grp[0]);
1 root 123
 
11 andreas 124
    // Now start our Thread
125
    if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
126
    {
127
        syslog(LOG_DAEMON | LOG_ERR, "Create of thread \"pthr_parser\" failed!");
128
        return 1;
129
    }
5 andreas 130
 
11 andreas 131
    pthread_detach(pthr_pars);
1 root 132
 
11 andreas 133
    // Here we start another thread to read the temperature
134
    if (pthread_create(&pthr_temp_pars, NULL, pthr_temperature, (void *)0) != 0)
135
    {
136
        syslog(LOG_DAEMON | LOG_ERR, "Create of thread \"pthr_temperature\" failed!");
137
        pthread_cancel(pthr_pars);
138
        return 1;
139
    }
140
 
141
    pthread_detach(pthr_temp_pars);
142
 
143
    // At least we neeed a thread to send the on/off status whenever it changes.
144
    if (pthread_create(&pthr_run_change, NULL, pthr_report, (void *)0) != 0)
145
    {
146
        syslog(LOG_DAEMON | LOG_ERR, "Create of thread \"pthr_report\" failed!");
147
        pthread_cancel(pthr_pars);
148
        pthread_cancel(pthr_temp_pars);
149
        return 1;
150
    }
151
 
152
    pthread_detach(pthr_run_change);
153
 
154
    while (!killed)
155
        sleep(1);
156
 
157
    unlink(configs.Pidfile);
158
    pthread_exit(NULL);
159
    return 0;
1 root 160
}
161
 
162
/*
163
 * Detach application from console and make it a daemon.
164
 */
11 andreas 165
void daemon_start(int ignsigcld)
1 root 166
{
11 andreas 167
    int childpid, fd;
168
    char hv0[64];
1 root 169
 
11 andreas 170
    if (getpid() == 1)
171
        goto out;
1 root 172
 
173
#ifdef SIGTTOU
11 andreas 174
    signal(SIGTTOU, SIG_IGN);
1 root 175
#endif
176
#ifdef SIGTTIN
11 andreas 177
    signal(SIGTTIN, SIG_IGN);
1 root 178
#endif
179
#ifdef SIGTSTP
11 andreas 180
    signal(SIGTSTP, SIG_IGN);
1 root 181
#endif
182
 
11 andreas 183
    if ((childpid = fork()) < 0)
184
        fprintf(stderr, "Can't fork this child\n");
185
    else if (childpid > 0)
186
        exit(0);
1 root 187
 
11 andreas 188
    if (setpgrp() == -1)
189
        fprintf(stderr, "Can't change process group\n");
1 root 190
 
11 andreas 191
    signal(SIGHUP, SIG_IGN);
1 root 192
 
11 andreas 193
    if ((childpid = fork()) < 0)
194
        syslog(LOG_DAEMON | LOG_ERR, "Can't fork second child");
195
    else if (childpid > 0)
196
        exit(0);             /* first child */
1 root 197
 
11 andreas 198
    /* second child */
1 root 199
out:
200
 
11 andreas 201
    for (fd = 0; fd < NOFILE; fd++)
202
        close(fd);
1 root 203
 
11 andreas 204
    errno = 0;
205
    chdir("/");
206
    umask(0);
10 andreas 207
 
11 andreas 208
    if (ignsigcld)
209
        signal(SIGCLD, SIG_IGN);
210
 
211
    signal(SIGTERM, sig_child);
212
    // Create PID file
213
    if ((fd = open(configs.Pidfile, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
214
    {
215
        syslog(LOG_DAEMON | LOG_WARNING, "Can't create PID file %s: %s", configs.Pidfile, strerror(errno));
216
        return;
217
    }
218
 
219
    sprintf(&hv0[0], "%d", getpid());
220
    write(fd, hv0, strlen(hv0));
221
    close(fd);
1 root 222
}
223
 
11 andreas 224
void sig_child(int)
1 root 225
{
11 andreas 226
    syslog(LOG_DAEMON | LOG_INFO, "Program was killed by signal 15!");
227
    killed = 1;
1 root 228
}
229
 
230
void changeToUser(char *usr, char *grp)
231
{
11 andreas 232
    gid_t gr_gid;
1 root 233
 
11 andreas 234
    if (usr && strlen(usr))
235
    {
236
        /* get uid */
237
        struct passwd *userpwd;
238
        struct group *usergrp;
1 root 239
 
11 andreas 240
        if ((userpwd = getpwnam(usr)) == NULL)
241
        {
242
            syslog(LOG_DAEMON | LOG_ERR, "no such user: %s", usr);
243
            exit(EXIT_FAILURE);
244
        }
1 root 245
 
11 andreas 246
        if (!grp || strlen(grp) || (usergrp = getgrnam(grp)) == NULL)
247
        {
248
            if (grp && strlen(grp))
249
                syslog(LOG_DAEMON | LOG_WARNING, "no such group: %s", grp);
1 root 250
 
11 andreas 251
            gr_gid = userpwd->pw_gid;
252
        }
253
        else
254
            gr_gid = usergrp->gr_gid;
1 root 255
 
11 andreas 256
        if (setgid(gr_gid) == -1)
257
        {
258
            syslog(LOG_DAEMON | LOG_ERR, "cannot setgid of user %s: %s", usr, strerror(errno));
259
            exit(EXIT_FAILURE);
260
        }
1 root 261
 
262
#ifdef _BSD_SOURCE
11 andreas 263
        /* init suplementary groups
264
         * (must be done before we change our uid)
265
         */
266
        if (initgroups(usr, gr_gid) == -1)
267
            syslog(LOG_DAEMON | LOG_ERR, "cannot init suplementary groups of user %s: %s", usr, strerror(errno));
1 root 268
#endif
269
 
11 andreas 270
        /* set uid */
271
        if (setuid(userpwd->pw_uid) == -1)
272
        {
273
            syslog(LOG_DAEMON | LOG_ERR, "cannot change to uid of user %s: %s\n", usr, strerror(errno));
274
            exit(EXIT_FAILURE);
275
        }
1 root 276
 
11 andreas 277
        if (userpwd->pw_dir)
278
            setenv("HOME", userpwd->pw_dir, 1);
279
    }
1 root 280
}
281
 
5 andreas 282
void *pthr_temperature(void *pV_data)
283
{
11 andreas 284
    time_t t, tstart;
285
    struct tm *zeit, sz;
286
    int sleep_sec;
5 andreas 287
 
11 andreas 288
    // Initialize the serial port.
289
    serial_set_method((strlen(configs.Device)) ? 1 : 0, configs.VID, configs.PID);
5 andreas 290
 
11 andreas 291
    if (strlen(configs.Device))
292
        serial_set_device(configs.Device);
5 andreas 293
 
11 andreas 294
    if (!serial_open())
295
        return NULL;
5 andreas 296
 
11 andreas 297
    if (serialDev.switch_fd == -1)
298
    {
299
        if (!serial_open())
300
            return NULL;
301
    }
5 andreas 302
 
11 andreas 303
    sleep(1);   // Give the other thread time to initialize the structure
5 andreas 304
 
11 andreas 305
    while (!killed)
306
    {
307
        HEIZINDEX *act;
5 andreas 308
 
11 andreas 309
        GetTemp();
310
        sleep_sec = 300;
5 andreas 311
 
11 andreas 312
        // Compare the actual temperature with the one that should be
313
        // and switch heating on or off
314
        if (HeizFirst)
315
        {
316
            int wday;
317
            unsigned long loctm;
5 andreas 318
 
11 andreas 319
            t = time(NULL);
320
            sleep_sec = 300 - (int)((t + 300L) % 300L);
321
            sleep_sec++;
322
            zeit = localtime(&t);
5 andreas 323
 
11 andreas 324
            wday = (zeit->tm_wday == 0) ? 7 : zeit->tm_wday;
325
            memset(&sz, 0, sizeof(struct tm));
326
            sz.tm_min = 0;
327
            sz.tm_hour = 0;
328
            sz.tm_mon = zeit->tm_mon;
329
            sz.tm_mday = zeit->tm_mday;
330
            sz.tm_year = zeit->tm_year;
331
            sz.tm_isdst = zeit->tm_isdst;
332
            tstart = mktime(&sz);
333
            loctm = (t - tstart) + 1; // Seconds since midnight
334
            act = HeizFirst;
7 andreas 335
 
11 andreas 336
            while (act)
337
            {
338
                if (act->heizung->wday == wday && loctm >= act->heizung->start && loctm <= act->heizung->end)
339
                {
340
                    if (ActTemperature == 9999.0)
341
                        SwitchOff();     // No temperature, no heating
342
                    else if (ActTemperature < 5.0)
343
                        SwitchOn();      // Make sure it will not freeze
344
                    else if (ActTemperature > 30.0)
345
                        SwitchOff();     // Don't over heat
346
                    else if (ActTemperature <= (act->heizung->temp - 0.5))
347
                        SwitchOn();
348
                    else if (ActTemperature > act->heizung->temp)
349
                        SwitchOff();
350
                }
5 andreas 351
 
11 andreas 352
                act = act->next;
353
            }
354
        }
355
        else
356
            syslog(LOG_DAEMON | LOG_INFO, "Structure not initialized!");
5 andreas 357
 
11 andreas 358
        sleep(sleep_sec);        // Wait 5 Minutes
359
    }
5 andreas 360
}
361
 
9 andreas 362
void *processCommands(void *pV_data)
1 root 363
{
11 andreas 364
    char ch, buf[128];
365
    int i, s1, s;
366
    struct SOCKETS *soc;
367
    struct timeval tv;
368
    tv.tv_sec = 1;
369
    tv.tv_usec = 0;
9 andreas 370
 
11 andreas 371
    soc = (struct SOCKETS *)pV_data;
372
    s1 = soc->newfd;
373
    s = soc->sockfd;
374
    memset(&buf[0], 0, sizeof(buf));
375
    i = 0;
376
    int bytes = 0;
377
    setsockopt(s1, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
9 andreas 378
 
11 andreas 379
    while ((bytes = read(s1, &ch, 1)) > 0 || bytes < 0)
380
    {
381
        if (killed)
382
            break;
9 andreas 383
 
11 andreas 384
        if ((bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) || bytes == 0)
385
            continue;
386
        else
387
            break;
9 andreas 388
 
11 andreas 389
        if (i < (sizeof(buf) - 1))
390
        {
391
            buf[i] = ch;
9 andreas 392
 
11 andreas 393
            if (ch == ';' || ch == 0x0d)
394
            {
395
                int pstat;
9 andreas 396
 
11 andreas 397
                if (!strncmp(buf, "quit", 4))
398
                    break;
9 andreas 399
 
11 andreas 400
                if ((pstat = parseCommand(s1, buf)) == 0)
401
                {
402
                    char hv0[128];
9 andreas 403
 
11 andreas 404
                    sprintf(&hv0[0], "INVALID COMMAND: %s;", buf);
405
                    write(s1, hv0, strlen(hv0));
406
                }
407
                else if (pstat == 2)
408
                    write(s1, "NAK;", 4);
409
                else
410
                    write(s1, "OK;", 3);
9 andreas 411
 
11 andreas 412
                memset(&buf[0], 0, sizeof(buf));
413
                i = 0;
9 andreas 414
 
11 andreas 415
                continue;
416
            }
417
        }
418
 
419
        i++;
420
    }
421
 
422
    soc->valid = 0;
423
    close(s1);
9 andreas 424
}
425
 
426
void *pthr_parser(void *pV_data)
427
{
11 andreas 428
    char str[INET_ADDRSTRLEN];
429
    // socket structure
430
    struct sockaddr_in client1, server1;
431
    struct servent *serviceInfo;
432
    int s1, s;
433
    socklen_t length;
434
    int optval = 1;
1 root 435
 
11 andreas 436
    memset(&client1, 0, sizeof(client1));
437
    memset(&server1, 0, sizeof(server1));
438
    // socket server
439
    if (configs.port > 0)
440
        server1.sin_port = htons(configs.port);
441
    else if ((serviceInfo = getservbyname("heizung", "tcp")))
442
        server1.sin_port = serviceInfo->s_port;
443
    else
444
    {
445
        syslog(LOG_DAEMON | LOG_ERR, "Error: No TCP port defined!");
446
        exit(EXIT_FAILURE);
447
    }
1 root 448
 
11 andreas 449
    server1.sin_family = AF_INET;
450
    server1.sin_addr.s_addr = INADDR_ANY;
1 root 451
 
11 andreas 452
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
453
    {
454
        syslog(LOG_DAEMON | LOG_ERR, "Error in socket: %s", strerror(errno));
455
        exit(EXIT_FAILURE);
456
    }
1 root 457
 
11 andreas 458
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) == -1)
459
    {
460
        syslog(LOG_DAEMON | LOG_ERR, "Error setting socket options: %s", strerror(errno));
461
        close(s);
462
        exit(EXIT_FAILURE);
463
    }
1 root 464
 
11 andreas 465
    if (bind(s, (struct sockaddr *)&server1, sizeof(server1)) < 0)
466
    {
467
        syslog(LOG_DAEMON | LOG_ERR, "Error in bind: %s", strerror(errno));
468
        close(s);
469
        exit(EXIT_FAILURE);
470
    }
1 root 471
 
11 andreas 472
    if (listen(s, 5) < 0)
473
    {
474
        syslog(LOG_DAEMON | LOG_ERR, "Error in listen: %s", strerror(errno));
475
        close(s);
476
        exit(EXIT_FAILURE);
477
    }
1 root 478
 
11 andreas 479
    length = sizeof(client1);
480
    syslog(LOG_DAEMON | LOG_DEBUG, "Server ready: %d", s);
1 root 481
 
11 andreas 482
    while (!killed)
483
    {
484
        // Read time table at every loop, to be sure to have the latest
485
        // version (User may change it at every time)
486
        if (!readHeizPlan())
487
        {
488
            if (!HeizFirst)
489
            {
490
                syslog(LOG_DAEMON | LOG_ERR, "Error reading table %s with plan!", configs.HeizPath);
491
                exit(EXIT_FAILURE);
492
            }
493
            else
494
                syslog(LOG_DAEMON | LOG_ERR, "Error reading table with plan! Will work with old one.");
495
        }
9 andreas 496
 
11 andreas 497
        if ((s1 = accept(s, (struct sockaddr *)&client1, &length)) < 0)
498
        {
499
            syslog(LOG_DAEMON | LOG_ERR, "Error in accept: %d: %s", s1, strerror(errno));
500
            continue;
501
        }
1 root 502
 
11 andreas 503
        inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
504
        syslog(LOG_DAEMON | LOG_INFO, "Connected to client %s", str);
505
        soc.sockfd = s;
506
        soc.newfd = s1;
507
        soc.valid = 1;
1 root 508
 
11 andreas 509
        if (pthread_create(&pthr_process, NULL, processCommands, (void *)&soc) != 0)
510
        {
511
            syslog(LOG_DAEMON | LOG_ERR, "Create of thread \"pthr_parser\" failed!");
512
            close(s1);
513
        }
1 root 514
 
11 andreas 515
        pthread_detach(pthr_process);
516
    }
517
 
518
    close(s1);
519
    close(s);
520
    serial_close();
521
    return NULL;
9 andreas 522
}
1 root 523
 
11 andreas 524
void *pthr_report(void *pV_data)
525
{
526
    int oldState = HeatStatus;
527
    char hv0[256];
528
 
529
    while(!killed)
530
    {
531
        if (soc.valid && oldState != HeatStatus)
532
        {
533
            snprintf(&hv0[0], sizeof(hv0), "HEATSTAT:%d;", HeatStatus);
534
            write(soc.newfd, &hv0[0], strlen(hv0));
535
            oldState = HeatStatus;
536
        }
537
 
538
        sleep(1);
539
    }
540
 
541
    return NULL;
542
}
543
 
9 andreas 544
void sendList(int s1)
545
{
11 andreas 546
    char hv0[256];
547
    int i, wday = 1;
548
    HEIZINDEX *act;
1 root 549
 
11 andreas 550
    // We will write out the week days in the correct order.
551
    // So we need 2 loops. The first one is runnung for every week day
552
    // and the 2nd one will find every entry for the actual week day.
553
    while (wday <= 7)
554
    {
555
        act = HeizFirst;
556
        i = 1;       // The line counter --> reset it to 1 for every week day
1 root 557
 
11 andreas 558
        while (act)
559
        {
560
            if (act->heizung->wday == wday)
561
            {
562
                sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
563
                        act->heizung->start, act->heizung->end, act->heizung->temp);
564
                write(s1, &hv0[0], strlen(hv0));
565
                i++;
566
            }
1 root 567
 
11 andreas 568
            act = act->next;
569
        }
1 root 570
 
11 andreas 571
        wday++;
572
    }
9 andreas 573
}
1 root 574
 
9 andreas 575
int listSchemas(int s1)
576
{
11 andreas 577
    char hv0[256];
578
    DIR *dir;
579
    struct dirent *de;
580
    char *ldup, *lp;
9 andreas 581
 
11 andreas 582
    if ((ldup = strdup(configs.HeizPath)) == NULL)
583
    {
584
        syslog(LOG_DAEMON | LOG_ERR, "Not enough memory for path: %s", strerror(errno));
585
        return 2;
586
    }
9 andreas 587
 
11 andreas 588
    lp = dirname(ldup);
9 andreas 589
 
11 andreas 590
    if ((dir = opendir(lp)) == NULL)
591
    {
592
        syslog(LOG_DAEMON | LOG_ERR, "Could not open directory %s: %s", lp, strerror(errno));
593
        free(ldup);
594
        return 2;
595
    }
9 andreas 596
 
11 andreas 597
    strcpy(&hv0[0], "SCHEMA START;");
598
    write(s1, hv0, strlen(hv0));
9 andreas 599
 
11 andreas 600
    while ((de = readdir(dir)) != NULL)
601
    {
602
        if (de->d_type == DT_REG && strstr(de->d_name, ".heat") != NULL)
603
        {
604
            char dn[256], *p;
9 andreas 605
 
11 andreas 606
            memset(&dn[0], 0, sizeof(dn));
607
            strncpy(dn, de->d_name, sizeof(dn) - 1);
9 andreas 608
 
11 andreas 609
            if ((p = strrchr(dn, '.')) != NULL)
610
                * p = 0;
611
            else
612
                continue;
9 andreas 613
 
11 andreas 614
            sprintf(&hv0[0], "SCHEMA FILE:%s;", dn);
615
            write(s1, hv0, strlen(hv0));
616
        }
617
    }
9 andreas 618
 
11 andreas 619
    closedir(dir);
620
    free(ldup);
621
    return 1;
1 root 622
}
623
 
624
int parseCommand(int s1, char *cmd)
625
{
11 andreas 626
    char bef[32], par[1024], *p;
627
    char hv0[256];
628
    int i, j;
629
    HEIZINDEX *act, *last;
1 root 630
 
11 andreas 631
    memset(bef, 0, sizeof(bef));
632
    memset(par, 0, sizeof(par));
1 root 633
 
11 andreas 634
    if ((p = strchr(cmd, ':')) != NULL)     // do we have a parameter?
635
    {
636
        strncpy(bef, cmd, p - cmd);
637
        strncpy(par, p + 1, strlen(p) - 2); // Cut off the trailing semi colon
638
    }
639
    else
640
        strncpy(bef, cmd, strlen(cmd) - 1);
5 andreas 641
 
11 andreas 642
    if (!strcasecmp(bef, "LIST"))       // Write out current list
643
        sendList(s1);
1 root 644
 
11 andreas 645
    if (!strcasecmp(bef, "GET WDAY"))       // Write out a particular week day
646
    {
647
        int wday = atoi(par);
1 root 648
 
11 andreas 649
        if (wday < 1 || wday > 7)
650
        {
651
            strcpy(&hv0[0], "ERROR:Invalid week day");
652
            write(s1, &hv0[0], strlen(hv0));
653
            return 0;
654
        }
1 root 655
 
11 andreas 656
        act = HeizFirst;
1 root 657
 
11 andreas 658
        while (act && act->heizung->wday != wday)
659
            act = act->next;
1 root 660
 
11 andreas 661
        if (!act)
662
        {
663
            sprintf(&hv0[0], "ERROR:No plan for week day %d", wday);
664
            write(s1, &hv0[0], strlen(hv0));
665
            return 0;
666
        }
1 root 667
 
11 andreas 668
        i = 1;
1 root 669
 
11 andreas 670
        while (act && act->heizung->wday == wday)
671
        {
672
            sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
673
                    act->heizung->start, act->heizung->end, act->heizung->temp);
674
            write(s1, &hv0[0], strlen(hv0));
675
            i++;
676
            act = act->next;
677
        }
678
    }
1 root 679
 
11 andreas 680
    if (!strcasecmp(bef, "GET TEMP"))   // Return actual temperature
681
    {
682
        sprintf(&hv0[0], "TEMP:%.1f;", ActTemperature);
683
        write(s1, &hv0[0], strlen(hv0));
684
    }
1 root 685
 
11 andreas 686
    if (!strcasecmp(bef, "GET PRESSURE"))   // Return the actual air pressure
687
    {
688
        sprintf(&hv0[0], "PRESSURE:%.1f;", ActPressure);
689
        write(s1, &hv0[0], strlen(hv0));
690
    }
1 root 691
 
11 andreas 692
    if (!strcasecmp(bef, "HEATSTAT"))   // Return the status of the heating
693
    {
694
        sprintf(&hv0[0], "HEATSTAT:%d;", HeatStatus);
695
        write(s1, &hv0[0], strlen(hv0));
696
    }
8 andreas 697
 
11 andreas 698
    // SET DAY:<count>:<day>:<end1>:<temp>[:<end2>:<temp>[:...]];
699
    // <count>   number of entries following
700
    // <day>     The day of the week
701
    // <end1>    The end time
702
    // <temp>    The temperature wanted until end time is reached
703
    //
704
    if (!strcasecmp(bef, "SET DAY"))        // Set the plan for a particular day
705
    {
706
        int count, wday, i;
707
        long endt;
708
        float temp;
2 andreas 709
 
11 andreas 710
        count = atoi(par);
711
        remove_string(par, ":", &hv0[0]);
712
        wday = atoi(par);
713
        freeDay(wday);
714
        last = NULL;
2 andreas 715
 
11 andreas 716
        if (count > 0)
717
        {
718
            for (i = 0; i < count; i++)
719
            {
720
                remove_string(par, ":", &hv0[0]);
721
                endt = atol(par);
722
                remove_string(par, ":", &hv0[0]);
723
                temp = atof(par);
2 andreas 724
 
11 andreas 725
                if ((act = allocateMemory()) == NULL)
726
                {
727
                    syslog(LOG_DAEMON, "Error allocating memory for a temperature line.");
728
                    sprintf(&hv0[0], "ERROR:Not enough memory!");
729
                    write(s1, &hv0[0], strlen(hv0));
730
                    return 2;
731
                }
2 andreas 732
 
11 andreas 733
                act->heizung->wday = wday;
734
                act->heizung->end = endt;
735
                act->heizung->temp = temp;
4 andreas 736
 
11 andreas 737
                if (last)
738
                    act->heizung->start = last->heizung->end;
739
                else
740
                    act->heizung->start = 0L;
4 andreas 741
 
11 andreas 742
                last = act;
743
            }
5 andreas 744
 
11 andreas 745
            writeHeizPlan();
746
        }
747
    }
1 root 748
 
11 andreas 749
    // SET TEMP:<wday>:<end>:<temp>;
750
    if (!strcasecmp(bef, "SET TEMP"))   // Set the temperature for a particular day and line
751
    {
752
        int wday;
753
        unsigned long endt;
754
        float tmp;
1 root 755
 
11 andreas 756
        wday = atoi(par);
757
        remove_string(par, ":", &hv0[0]);
758
        endt = atol(par);
759
        remove_string(par, ":", &hv0[0]);
760
        tmp = atof(par);
761
        insertMember(wday, endt, tmp);
762
        writeHeizPlan();
763
    }
1 root 764
 
11 andreas 765
    // SAVE SCHEMA:<name>;
766
    if (!strcasecmp(bef, "SAVE SCHEMA"))    // Save the current heating plan into a file
767
    {
768
        sprintf(&hv0[0], "%s.heat", par);
9 andreas 769
 
11 andreas 770
        if (!writeHeizPlanName(hv0))
771
            return 2;
772
    }
9 andreas 773
 
11 andreas 774
    // LOAD SCHEMA:<name>;
775
    if (!strcasecmp(bef, "LOAD SCHEMA"))    // Load a previous saved schema from a file
776
    {
777
        sprintf(&hv0[0], "%s.heat", par);
9 andreas 778
 
11 andreas 779
        if (!readHeizPlanName(hv0))
780
            return 2;
9 andreas 781
 
11 andreas 782
        // write the new schema immediately to the default file
783
        writeHeizPlan();
784
        sendList(s1);
785
    }
9 andreas 786
 
11 andreas 787
    // DELETE SCHEMA:<name>;
788
    if (!strcasecmp(bef, "DELETE SCHEMA"))  // Delete an existing schema
789
    {
790
        makeFileName(&hv0[0], par, sizeof(hv0));
791
 
792
        if (strlen(hv0) < (sizeof(hv0) - 6))
793
            strcat(&hv0[0], ".heat");
794
        else
9 andreas 795
        {
11 andreas 796
            syslog(LOG_DAEMON, "File name too large for internal buffer");
797
            return 2;
798
        }
9 andreas 799
 
11 andreas 800
        if (access(hv0, R_OK | W_OK))
801
        {
802
            syslog(LOG_DAEMON, "No access to file %s: %s", hv0, strerror(errno));
803
            return 2;
804
        }
9 andreas 805
 
11 andreas 806
        if (unlink(hv0) == -1)
807
        {
808
            syslog(LOG_DAEMON, "Error deleting file %s: %s", hv0, strerror(errno));
809
            return 2;
810
        }
9 andreas 811
 
11 andreas 812
        return listSchemas(s1);
813
    }
9 andreas 814
 
11 andreas 815
    // LIST SCHEMA;
816
    if (!strcasecmp(bef, "LIST SCHEMA"))    // Return a list of all available schamas
817
        return listSchemas(s1);
9 andreas 818
 
11 andreas 819
    return 1;
1 root 820
}
821
 
822
/*
2 andreas 823
 * Remove a complete day
824
 */
825
void freeDay(int wday)
826
{
11 andreas 827
    HEIZINDEX *act, *next, *prev;
2 andreas 828
 
11 andreas 829
    act = HeizFirst;
830
    prev = NULL;
2 andreas 831
 
11 andreas 832
    while (act)
833
    {
834
        if (act->heizung->wday == wday)
835
            act = freeChainMember(act);
2 andreas 836
 
11 andreas 837
        act = act->next;
838
    }
2 andreas 839
}
840
 
841
/*
842
 * Insert a new entry
843
 */
844
void insertMember(int wday, long endt, float temp)
845
{
11 andreas 846
    HEIZINDEX *act;
2 andreas 847
 
11 andreas 848
    act = HeizFirst;
2 andreas 849
 
11 andreas 850
    while (act)
851
    {
852
        if (act->heizung->wday == wday && act->heizung->end == endt)
853
        {
854
            act->heizung->temp = temp;
855
            return;
856
        }
2 andreas 857
 
11 andreas 858
        act = act->next;
859
    }
2 andreas 860
 
11 andreas 861
    if ((act = allocateMemory()) != NULL)
862
    {
863
        act->heizung->wday = wday;
864
        act->heizung->end = endt;
865
        act->heizung->temp = temp;
866
    }
2 andreas 867
}
868
 
869
/*
1 root 870
 * Free the memory for the actual heizung plan.
871
 */
872
void freeMemory()
873
{
11 andreas 874
    HEIZINDEX *act, *next;
1 root 875
 
11 andreas 876
    act = HeizFirst;
1 root 877
 
11 andreas 878
    while (act)
879
    {
880
        if (act->heizung)
881
        {
882
            free(act->heizung);
883
            act->heizung = NULL;
884
        }
1 root 885
 
11 andreas 886
        next = act->next;
887
        free(act);
888
        act = next;
889
    }
5 andreas 890
 
11 andreas 891
    HeizFirst = NULL;
1 root 892
}
893
 
894
/*
895
 * Free only one entry in the chain
896
 */
2 andreas 897
HEIZINDEX *freeChainMember(HEIZINDEX *member)
1 root 898
{
11 andreas 899
    HEIZINDEX *act, *prev;
1 root 900
 
11 andreas 901
    act = HeizFirst;
902
    prev = NULL;
1 root 903
 
11 andreas 904
    while (act != member)
905
    {
906
        prev = act;
907
        act = act->next;
908
    }
1 root 909
 
11 andreas 910
    if (act == member)
911
    {
912
        if (prev)
913
            prev->next = act->next;
914
        else
915
        {
916
            prev = act->next;
1 root 917
 
11 andreas 918
            if (act == HeizFirst)
919
                HeizFirst = act->next;
920
        }
4 andreas 921
 
11 andreas 922
        if (act->heizung)
923
            free(act->heizung);
1 root 924
 
11 andreas 925
        free(act);
926
        return prev;
927
    }
2 andreas 928
 
11 andreas 929
    return NULL;
1 root 930
}
931
 
932
/*
4 andreas 933
 * Allocate the memory for the actual heizung plan.
934
 * This function appends an element to the end of the chain.
1 root 935
 */
936
HEIZINDEX *allocateMemory()
937
{
11 andreas 938
    HEIZINDEX *act, *last;
1 root 939
 
11 andreas 940
    if (!HeizFirst)
941
    {
942
        HeizFirst = malloc(sizeof(HEIZINDEX));
1 root 943
 
11 andreas 944
        if (HeizFirst)
945
        {
946
            HeizFirst->heizung = malloc(sizeof(HEIZUNG));
947
            HeizFirst->next = NULL;
948
        }
949
        else
950
            return NULL;
1 root 951
 
11 andreas 952
        return HeizFirst;
953
    }
954
    else
955
    {
956
        // Find last element
957
        last = HeizFirst;
1 root 958
 
11 andreas 959
        while (last->next)
960
            last = last->next;
1 root 961
 
11 andreas 962
        act = malloc(sizeof(HEIZINDEX));
1 root 963
 
11 andreas 964
        if (act)
965
        {
966
            act->heizung = malloc(sizeof(HEIZUNG));
967
            act->next = NULL;
968
            last->next = act;
969
        }
970
        else
971
            return NULL;
1 root 972
 
11 andreas 973
        return act;
974
    }
1 root 975
 
11 andreas 976
    return NULL;
1 root 977
}
978
 
979
/*
4 andreas 980
 * Allocate the memory for the actual heizung plan.
981
 * This function inserts an element into the chain.
982
 */
983
HEIZINDEX *insertMemory(HEIZINDEX *pos)
984
{
11 andreas 985
    HEIZINDEX *act;
4 andreas 986
 
11 andreas 987
    if (!HeizFirst)
988
    {
989
        HeizFirst = malloc(sizeof(HEIZINDEX));
4 andreas 990
 
11 andreas 991
        if (HeizFirst)
992
        {
993
            HeizFirst->heizung = malloc(sizeof(HEIZUNG));
994
            HeizFirst->next = NULL;
995
        }
996
        else
997
            return NULL;
4 andreas 998
 
11 andreas 999
        return HeizFirst;
1000
    }
1001
    else
1002
    {
1003
        act = malloc(sizeof(HEIZINDEX));
4 andreas 1004
 
11 andreas 1005
        if (act)
1006
        {
1007
            act->heizung = malloc(sizeof(HEIZUNG));
1008
            act->next = pos->next;
1009
            pos->next = act;
1010
        }
1011
        else
1012
            return NULL;
4 andreas 1013
 
11 andreas 1014
        return act;
1015
    }
4 andreas 1016
 
11 andreas 1017
    return NULL;
4 andreas 1018
}
1019
 
1020
/*
1 root 1021
 * The following functions read a config file and put the
1022
 * contents into a structure.
1023
 */
1024
void readConf(void)
1025
{
11 andreas 1026
    int fd;
1027
    char confFile[512], line[512];
1028
    char *home, *p;
1029
    char hv0[64], hv1[128];
1 root 1030
 
11 andreas 1031
    home = getenv("HOME");
1032
    fd = -1;
1 root 1033
 
11 andreas 1034
    if (!access("/etc/heizung.conf", R_OK))
1035
        strcpy(confFile, "/etc/heizung.conf");
1036
    else if (!access("/etc/heizung/heizung.conf", R_OK))
1037
        strcpy(confFile, "/etc/heizung/heizung.conf");
1038
    else if (!access("/usr/etc/heizung.conf", R_OK))
1039
        strcpy(confFile, "/usr/etc/heizung.conf");
1040
    else if (home)
1041
    {
1042
        strcpy(confFile, home);
1043
        strcat(confFile, "/.heizung.conf");
1 root 1044
 
11 andreas 1045
        if (access(confFile, R_OK))
1046
        {
1047
            syslog(LOG_DAEMON | LOG_WARNING, "Even config file %s was not found!", confFile);
1048
            confFile[0] = 0;
1049
        }
1050
    }
1051
    else
1052
        confFile[0] = 0;
1 root 1053
 
11 andreas 1054
    memset(&configs, 0, sizeof(configs));
1055
    strcpy(configs.User, "nobody");
1056
    strcpy(configs.Grp, "nobody");
1057
    strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
1058
    strcpy(configs.Werte, "/var/log/werte.dat");
1059
    strcpy(configs.Device, "/dev/ttyS1");
1060
    strcpy(configs.Pidfile, "/run/heizung.pid");
1061
#ifdef SENSOR_ETHERNET
1062
    strcpy(configs.IP, "0.0.0.0");
1063
#endif
1064
    configs.port = 11001;
1 root 1065
 
11 andreas 1066
    if (!confFile[0] || (fd = open(confFile, O_RDONLY)) == -1)
1067
    {
1068
        if (confFile[0])
1069
            syslog(LOG_DAEMON | LOG_WARNING, "Error opening the config file %s! Using built in defaults. (%s)", confFile, strerror(errno));
1070
        else
1071
            syslog(LOG_DAEMON | LOG_WARNING, "Error opening the config file! Using built in defaults.");
4 andreas 1072
 
11 andreas 1073
        return;
1074
    }
1 root 1075
 
11 andreas 1076
    while (readLine(fd, &line[0], sizeof(line)) != NULL)
1077
    {
1078
        int len;
1 root 1079
 
11 andreas 1080
        trim(&line[0]);
1 root 1081
 
11 andreas 1082
        if (line[0] == '#' || !strlen(line))
1083
            continue;
1 root 1084
 
11 andreas 1085
        if ((p = strchr(line, '=')) == NULL)
1086
            continue;
1 root 1087
 
11 andreas 1088
        *p = 0;
1089
        p++;
1090
        len = strlen(line);
1 root 1091
 
11 andreas 1092
        if (len > sizeof(hv0))
1093
            len = sizeof(hv0) - 1;
1 root 1094
 
11 andreas 1095
        strncpy(hv0, line, len);
1096
        hv0[len] = 0;
1097
        trim(hv0);
1098
        len = strlen(p);
1 root 1099
 
11 andreas 1100
        if (len > sizeof(hv1))
1101
            len = sizeof(hv0) - 1;
1 root 1102
 
11 andreas 1103
        strncpy(hv1, p, len);
1104
        hv1[len] = 0;
1105
        trim(hv1);
1 root 1106
 
11 andreas 1107
        if (!strcasecmp(hv0, "user"))
1108
        {
1109
            syslog(LOG_DAEMON | LOG_INFO, "Found \"user\": %s", hv1);
1110
            strncpy(configs.User, hv1, sizeof(configs.User));
1111
        }
1 root 1112
 
11 andreas 1113
        if (!strcasecmp(hv0, "group"))
1114
        {
1115
            syslog(LOG_DAEMON | LOG_INFO, "Found \"group\": %s", hv1);
1116
            strncpy(configs.Grp, hv1, sizeof(configs.Grp));
1117
        }
1 root 1118
 
11 andreas 1119
        if (!strcasecmp(hv0, "port"))
1120
        {
1121
            syslog(LOG_DAEMON | LOG_INFO, "Found \"port\": %s", hv1);
1122
            configs.port = atoi(hv1);
1123
        }
1 root 1124
 
11 andreas 1125
        if (!strcasecmp(hv0, "heizpath"))
1126
        {
1127
            syslog(LOG_DAEMON | LOG_INFO, "Found \"heizpath\": %s", hv1);
1128
            strncpy(configs.HeizPath, hv1, sizeof(configs.HeizPath));
1129
        }
5 andreas 1130
 
11 andreas 1131
        if (!strcasecmp(hv0, "Werte"))
1132
        {
1133
            syslog(LOG_DAEMON | LOG_INFO, "Found \"Werte\": %s", hv1);
1134
            strncpy(configs.Werte, hv1, sizeof(configs.Werte));
1135
        }
5 andreas 1136
 
11 andreas 1137
        if (!strcasecmp(hv0, "Device"))
1138
        {
1139
            syslog(LOG_DAEMON | LOG_INFO, "Found \"Device\": %s", hv1);
1140
            strncpy(configs.Device, hv1, sizeof(configs.Device));
1141
        }
5 andreas 1142
 
11 andreas 1143
        if (!strcasecmp(hv0, "Pidfile"))
1144
        {
1145
            syslog(LOG_DAEMON | LOG_INFO, "Found \"Pidfile\": %s", hv1);
1146
            strncpy(configs.Pidfile, hv1, sizeof(configs.Pidfile));
1147
        }
10 andreas 1148
 
11 andreas 1149
        if (!strcasecmp(hv0, "VID"))
1150
        {
1151
            syslog(LOG_DAEMON | LOG_INFO, "Found VendorID: %04x", atoi(hv1));
1152
            configs.VID = atoi(hv1);
1153
        }
5 andreas 1154
 
11 andreas 1155
        if (!strcasecmp(hv0, "PID"))
1156
        {
1157
            syslog(LOG_DAEMON | LOG_INFO, "Found ProductID: %04x", atoi(hv1));
1158
            configs.PID = atoi(hv1);
1159
        }
1 root 1160
 
11 andreas 1161
#ifdef SENSOR_ETHERNET
1162
        if (!strcasecmp(hv0, "IP"))
1163
        {
1164
            syslog(LOG_DAEMON | LOG_INFO, "Found IP: %s", hv1);
1165
            strncpy(configs.IP, hv1, sizeof(configs.IP));
1166
        }
1167
 
1168
        if (!strcasecmp(hv0, "TempPort"))
1169
        {
1170
            syslog(LOG_DAEMON | LOG_INFO, "Found temperature network port %d", atoi(hv1));
1171
            configs.TempPort = atoi(hv1);
1172
        }
1173
#endif
1174
    }
1175
 
1176
    close(fd);
1 root 1177
}
1178
 
9 andreas 1179
char *makeFileName(char *ret, char *fname, int len)
1180
{
11 andreas 1181
    char *fdup, *pright, *cfg, *lcfg;
9 andreas 1182
 
11 andreas 1183
    if (ret == NULL || fname == NULL || !len || !strlen(fname))
1184
        return NULL;
9 andreas 1185
 
11 andreas 1186
    if (!access(fname, R_OK | W_OK))
1187
    {
1188
        memset(ret, 0, len);
1189
        strncpy(ret, fname, len - 1);
1190
        return ret;
1191
    }
9 andreas 1192
 
11 andreas 1193
    fdup = strdup(fname);
9 andreas 1194
 
11 andreas 1195
    if (fdup == NULL)
1196
    {
1197
        syslog(LOG_DAEMON | LOG_ERR, "Error allocating memory: %s", strerror(errno));
1198
        return NULL;
1199
    }
9 andreas 1200
 
11 andreas 1201
    cfg = strdup(configs.HeizPath);
9 andreas 1202
 
11 andreas 1203
    if (cfg == NULL)
1204
    {
1205
        syslog(LOG_DAEMON | LOG_ERR, "Error allocating memory: %s", strerror(errno));
1206
        free(fdup);
1207
        return NULL;
1208
    }
1209
 
1210
    pright = basename(fdup);
1211
    lcfg = dirname(cfg);
1212
    memset(ret, 0, len);
1213
    strncpy(ret, lcfg, len - 1);
1214
 
1215
    if (strlen(ret) < (len - (strlen(pright) + 2)))
1216
    {
1217
        strcat(ret, "/");
1218
        strcat(ret, pright);
1219
        free(fdup);
1220
        free(cfg);
1221
        return ret;
1222
    }
1223
 
1224
    free(fdup);
1225
    free(cfg);
1226
    return NULL;
9 andreas 1227
}
1228
 
1 root 1229
int readHeizPlan(void)
1230
{
11 andreas 1231
    return readHeizPlanName(configs.HeizPath);
9 andreas 1232
}
1233
 
1234
int readHeizPlanName(char* fname)
1235
{
11 andreas 1236
    int fd, i, wday;
1237
    ulong tim;
1238
    char line[512];
1239
    char *p, *xp;
1240
    char hv0[64], hv1[128];
1241
    char path[512];
1242
    float temperature;
1243
    int counter = 0;
1244
    HEIZINDEX *act, *prev;
1 root 1245
 
11 andreas 1246
    fd = -1;
1 root 1247
 
11 andreas 1248
    if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
1249
    {
1250
        syslog(LOG_DAEMON | LOG_ERR, "No or invalid path %s", fname);
1251
        return 0;
1252
    }
1 root 1253
 
11 andreas 1254
    if (access(path, R_OK))
1255
    {
1256
        syslog(LOG_DAEMON | LOG_ERR, "Access to file %s denied: %s", path, strerror(errno));
1257
        return 0;
1258
    }
1 root 1259
 
11 andreas 1260
    if ((fd = open(path, O_RDONLY)) == -1)
1261
    {
1262
        syslog(LOG_DAEMON | LOG_ERR, "Error opening file %s: %s", path, strerror(errno));
1263
        return 0;
1264
    }
9 andreas 1265
 
11 andreas 1266
    act = prev = NULL;
1267
    freeMemory();
4 andreas 1268
 
11 andreas 1269
    while (readLine(fd, &line[0], sizeof(line)) != NULL)
1270
    {
1271
        int len;
1 root 1272
 
11 andreas 1273
        trim(&line[0]);
1 root 1274
 
11 andreas 1275
        if (line[0] == '#' || !strlen(line) || strchr(line, ',') == NULL)
1276
            continue;
1 root 1277
 
11 andreas 1278
        // We need a place to store the information
1279
        prev = act;
4 andreas 1280
 
11 andreas 1281
        if ((act = allocateMemory()) == NULL)
1282
        {
1283
            close(fd);
1284
            syslog(LOG_DAEMON | LOG_ERR, "Error allocating memory for a temperature line! Stopped reading file %s.", configs.HeizPath);
1285
            return 0;
1286
        }
4 andreas 1287
 
11 andreas 1288
        counter++;
1289
        memset(act->heizung, 0, sizeof(HEIZUNG));
1290
        // Parse a line. The line has the format:
1291
        // <wday>,<end>,<temperature>
1292
        p = strtok(line, ",");
1293
        i = 1;
1 root 1294
 
11 andreas 1295
        while (p)
1296
        {
1297
            switch (i)
1298
            {
1299
                case 1:    // Week day
1300
                    wday = atoi(p);
1 root 1301
 
11 andreas 1302
                    if (wday < 1 || wday > 7)   // valid line?
1303
                    {
1304
                        if (prev)
1305
                            wday = prev->heizung->wday;
1 root 1306
 
11 andreas 1307
                        if (wday < 1 || wday > 7)
1308
                        {
1309
                            p = strtok(NULL, ",");
1310
                            i++;
1311
                            continue;
1312
                        }
1313
                    }
1 root 1314
 
11 andreas 1315
                    act->heizung->wday = wday;
1316
                    act->heizung->start = 0;
1317
                    break;
1 root 1318
 
11 andreas 1319
                case 2:    // start/end time
1320
                    if ((xp = strchr(p, ':')) != NULL)
1321
                    {
1322
                        int hour, min;
1 root 1323
 
11 andreas 1324
                        hour = atoi(p);
1325
                        min = atoi(xp + 1);
1 root 1326
 
11 andreas 1327
                        if (hour >= 0 && hour <= 23 && min >= 0 && min <= 59)
1328
                        {
1329
                            act->heizung->end = hour * 3600 + min * 60 + 59;
1 root 1330
 
11 andreas 1331
                            if (prev && prev->heizung->wday == act->heizung->wday)
1332
                                act->heizung->start = prev->heizung->end;
1333
                        }
1334
                    }
1 root 1335
 
11 andreas 1336
                    break;
1 root 1337
 
11 andreas 1338
                case 3:    // temperature
1339
                    temperature = atof(p);
1 root 1340
 
11 andreas 1341
                    if (temperature < 5.0 || temperature > 30.0)
1342
                    {
1343
                        p = strtok(NULL, ",");
1344
                        i++;
1345
                        continue;
1346
                    }
1 root 1347
 
11 andreas 1348
                    act->heizung->temp = temperature;
1349
                    break;
1350
            }
1 root 1351
 
11 andreas 1352
            p = strtok(NULL, ",");
1353
            i++;
1354
        }
1355
    }
1356
 
1357
    syslog(LOG_DAEMON | LOG_INFO, "Found %d entries in %s", counter, configs.HeizPath);
1358
    close(fd);
1359
    return 1;
1 root 1360
}
1361
 
1362
/*
1363
 * write the (may be) altered plan.
1364
 * This function allways writes whole plan.
1365
 */
1366
int writeHeizPlan()
1367
{
11 andreas 1368
    return writeHeizPlanName(configs.HeizPath);
9 andreas 1369
}
1370
 
1371
int writeHeizPlanName(char *fname)
1372
{
11 andreas 1373
    int fd, wday;
1374
    ulong lastEnd;
1375
    char hv0[128], path[512];
1376
    HEIZINDEX *act;
1 root 1377
 
11 andreas 1378
    fd = -1;
1 root 1379
 
11 andreas 1380
    if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
1381
    {
1382
        syslog(LOG_DAEMON, "No or invalid path %s", fname);
1383
        return 0;
1384
    }
1 root 1385
 
11 andreas 1386
    if (!sortTable())
1387
        return 0;
10 andreas 1388
 
11 andreas 1389
    if ((fd = open(path, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1390
    {
1391
        syslog(LOG_DAEMON | LOG_ERR, "Error opening/creating file %s: %s", path, strerror(errno));
1392
        return 0;
1393
    }
1 root 1394
 
11 andreas 1395
    // Sort table for week days
1396
    for (wday = 1; wday <= 7; wday++)
1397
    {
1398
        lastEnd = 0L;
1399
        act = HeizFirst;
1 root 1400
 
11 andreas 1401
        while (act)
1402
        {
1403
            if (act->heizung->wday == wday)
1404
            {
1405
                int hour, min;
5 andreas 1406
 
11 andreas 1407
                hour = act->heizung->end / 3600;
1408
                min = (act->heizung->end - (hour * 3600)) / 60;
5 andreas 1409
 
11 andreas 1410
                if (hour > 23)
1411
                {
1412
                    hour = 23;
1413
                    min = 59;
1414
                }
10 andreas 1415
 
11 andreas 1416
                sprintf(&hv0[0], "%d,%02d:%02d,%.1f\n",
1417
                        act->heizung->wday, hour, min,
1418
                        act->heizung->temp);
1419
                write(fd, &hv0[0], strlen(hv0));
1420
            }
10 andreas 1421
 
11 andreas 1422
            act = act->next;
1423
        }
1424
    }
10 andreas 1425
 
11 andreas 1426
    close(fd);
1427
    return 1;
10 andreas 1428
}
1429
 
1430
/*
1431
 * Allocate the memory for the actual heizung plan.
1432
 * This function appends an element to the end of the chain.
1433
 */
1434
HEIZINDEX *allocMem(HEIZINDEX *first)
1435
{
11 andreas 1436
    HEIZINDEX *act, *last;
10 andreas 1437
 
11 andreas 1438
    if (!first)
1439
    {
1440
        first = malloc(sizeof(HEIZINDEX));
10 andreas 1441
 
11 andreas 1442
        if (first)
1443
        {
1444
            first->heizung = malloc(sizeof(HEIZUNG));
1445
            first->next = NULL;
1446
        }
1447
        else
1448
            return NULL;
10 andreas 1449
 
11 andreas 1450
        return first;
1451
    }
1452
    else
1453
    {
1454
        // Find last element
1455
        last = first;
10 andreas 1456
 
11 andreas 1457
        while (last->next)
1458
            last = last->next;
10 andreas 1459
 
11 andreas 1460
        act = malloc(sizeof(HEIZINDEX));
10 andreas 1461
 
11 andreas 1462
        if (act)
1463
        {
1464
            act->heizung = malloc(sizeof(HEIZUNG));
1465
            act->next = NULL;
1466
            last->next = act;
1467
        }
1468
        else
1469
            return NULL;
10 andreas 1470
 
11 andreas 1471
        return act;
1472
    }
10 andreas 1473
 
11 andreas 1474
    return NULL;
10 andreas 1475
}
1476
 
1477
// This function sorts the table for week days and every into increasing
1478
// time stamps
1479
int sortTable()
1480
{
11 andreas 1481
    int count, actElement, wday, flag;
1482
    ulong end;
1483
    HEIZINDEX *first, *act, *p;
10 andreas 1484
 
11 andreas 1485
    if (!HeizFirst)
1486
        return 0;
10 andreas 1487
 
11 andreas 1488
    p = HeizFirst;
1489
    // Count the number of elements
1490
    count = 0;
10 andreas 1491
 
11 andreas 1492
    while (p)
1493
    {
1494
        if (p->heizung->wday)
1495
            count++;
10 andreas 1496
 
11 andreas 1497
        p = p->next;
1498
    }
10 andreas 1499
 
11 andreas 1500
    wday = 1;
1501
    act = first = NULL;
1502
    actElement = 0;
10 andreas 1503
 
11 andreas 1504
    // First loop sorts for week days
1505
    while (actElement < count && wday <= 7)
1506
    {
1507
        int cnt, pos;
10 andreas 1508
 
11 andreas 1509
        p = HeizFirst;
1510
        flag = 0;
1511
        // find number of entries for actual week day
1512
        cnt = 0;
1513
        act = HeizFirst;
10 andreas 1514
 
11 andreas 1515
        while (act)
1516
        {
1517
            if (act->heizung->wday == wday)
1518
                cnt++;
10 andreas 1519
 
11 andreas 1520
            act = act->next;
1521
        }
10 andreas 1522
 
11 andreas 1523
        pos = 0;
1524
        end = 0L;
10 andreas 1525
 
11 andreas 1526
        // Second loop sorts the actual week day for end times
1527
        while (p)
1528
        {
1529
            if (p->heizung->wday == wday && p->heizung->start == end && p->heizung->end > end)
1530
            {
1531
                actElement++;
1532
                flag = 1;
1533
                act = allocMem(first);
10 andreas 1534
 
11 andreas 1535
                if (!first)
1536
                    first = act;
10 andreas 1537
 
11 andreas 1538
                // Check for valid memory block
1539
                if (act)
1540
                {
1541
                    memmove(act->heizung, p->heizung, sizeof(HEIZUNG));
1542
                    end = p->heizung->end;
1543
                    pos++;
1544
                }
1545
                else
1546
                {
1547
                    syslog(LOG_DAEMON | LOG_ERR, "Error allocationg memory for sorting table: %s", strerror(errno));
1548
                    // Free allocated memory
1549
                    act = first;
10 andreas 1550
 
11 andreas 1551
                    while (act)
1552
                    {
1553
                        p = act->next;
1554
                        free(act->heizung);
1555
                        free(act);
1556
                        act = p;
1557
                    }
10 andreas 1558
 
11 andreas 1559
                    return 0;
1560
                }
1561
            }
5 andreas 1562
 
11 andreas 1563
            p = p->next;
10 andreas 1564
 
11 andreas 1565
            // If there are still members left, reset pointer and restart loop
1566
            if (!p && pos < (cnt - 1) && flag)
1567
            {
1568
                p = HeizFirst;
1569
                flag = 0;
1570
            }
1571
        }
5 andreas 1572
 
11 andreas 1573
        wday++;
1574
    }
1 root 1575
 
11 andreas 1576
    freeMemory();
1577
    HeizFirst = first;
1578
    return 1;
1 root 1579
}
1580
 
4 andreas 1581
char *readLine(int fd, char *buf, int bufLen)
1 root 1582
{
11 andreas 1583
    int i, end;
1584
    char ch, *p;
1 root 1585
 
11 andreas 1586
    if (fd <= 0)
1587
    {
1588
        syslog(LOG_DAEMON | LOG_ERR, "Function readLine was called with an invalid file descriptor of %d!", fd);
1589
        return NULL;
1590
    }
1 root 1591
 
11 andreas 1592
    i = end = 0;
1593
    p = buf;
1 root 1594
 
11 andreas 1595
    while (read(fd, &ch, 1) > 0)
1596
    {
1597
        end = 1;
1598
 
1599
        if (ch == 0x0a)
4 andreas 1600
        {
11 andreas 1601
            *p = 0;
1602
            return buf;
1603
        }
1 root 1604
 
11 andreas 1605
        if (ch == 0x0d)      // ignore this!
1606
            continue;
1 root 1607
 
11 andreas 1608
        if (i < (bufLen - 1))
1609
        {
1610
            *p = ch;
1611
            p++;
1612
            i++;
4 andreas 1613
        }
11 andreas 1614
    }
1 root 1615
 
11 andreas 1616
    *p = 0;
4 andreas 1617
 
11 andreas 1618
    if (end)
1619
        return buf;
1620
    else
1621
        return NULL;
1 root 1622
}
1623
 
1624
char *trim(char *str)
1625
{
11 andreas 1626
    char *p1, *p2, *p;
1 root 1627
 
11 andreas 1628
    if (!str)
1629
        return NULL;
1 root 1630
 
11 andreas 1631
    if (!strlen(str))
1632
        return str;
4 andreas 1633
 
11 andreas 1634
    p = str;
1635
    p1 = p2 = NULL;
1 root 1636
 
11 andreas 1637
    while (*p)
1638
    {
1639
        if (!p1 && *p != ' ')
1640
        {
1641
            p1 = p;
1642
            break;
1643
        }
1 root 1644
 
11 andreas 1645
        p++;
1646
    }
1 root 1647
 
11 andreas 1648
    p2 = str + (strlen(str) - 1);
1 root 1649
 
11 andreas 1650
    while (p2 > str && *p2 == ' ')
1651
        p2--;
1 root 1652
 
11 andreas 1653
    if (p2)
1654
        *(p2 + 1) = 0;
1 root 1655
 
11 andreas 1656
    if (p1)
1657
    {
1658
        char *buf = strdup(p1);
1659
        strcpy(str, buf);
1660
        free(buf);
1661
    }
1 root 1662
 
11 andreas 1663
    return str;
1 root 1664
}
2 andreas 1665
 
1666
char *remove_string(char *str, char *search, char *ret)
1667
{
11 andreas 1668
    char *p;
2 andreas 1669
 
11 andreas 1670
    if (!strlen(str) || !strlen(search))
1671
        return NULL;
2 andreas 1672
 
11 andreas 1673
    if ((p = strstr(str, search)) != NULL)
1674
    {
1675
        int len = strlen(search);
2 andreas 1676
 
11 andreas 1677
        strncpy(ret, str, p - str + len);
1678
        ret[p - str + len] = 0;
1679
        memmove(str, p + len, strlen(p + len));
1680
        str[strlen(p + len)] = 0;
1681
        return ret;
1682
    }
2 andreas 1683
 
11 andreas 1684
    return NULL;
4 andreas 1685
}