|
1 #ifndef HAVE_RETAIN |
|
2 #include <stdio.h> |
|
3 #include <stdint.h> |
|
4 #include <unistd.h> |
|
5 #include "iec_types.h" |
|
6 |
|
7 int GetRetainSize(); |
|
8 |
|
9 /* Retain buffer. */ |
|
10 FILE *retain_buffer; |
|
11 const char rb_file[] = "retain_buffer_file"; |
|
12 const char rb_file_bckp[] = "retain_buffer_file.bak"; |
|
13 |
|
14 |
|
15 /* Retain header struct. */ |
|
16 struct retain_info_t { |
|
17 uint32_t retain_size; |
|
18 uint32_t hash_size; |
|
19 uint8_t* hash; |
|
20 uint32_t header_offset; |
|
21 uint32_t header_crc; |
|
22 }; |
|
23 |
|
24 /* Init retain info structure. */ |
|
25 struct retain_info_t retain_info; |
|
26 |
|
27 /* CRC lookup table and initial state. */ |
|
28 uint32_t crc32_table[256]; |
|
29 uint32_t retain_crc; |
|
30 |
|
31 |
|
32 /* Generate CRC32 lookup table. */ |
|
33 void GenerateCRC32Table(void) |
|
34 { |
|
35 unsigned int i, j; |
|
36 /* Use CRC-32-IEEE 802.3 polynomial 0x04C11DB7 (bit reflected). */ |
|
37 uint32_t poly = 0xEDB88320; |
|
38 |
|
39 for (i = 0; i <= 0xFF; i++) |
|
40 { |
|
41 uint32_t c = i; |
|
42 for (j = 0 ; j < 8 ; j++) |
|
43 c = (c & 1) ? (c >> 1 ) ^ poly : (c >> 1); |
|
44 crc32_table[i] = c; |
|
45 } |
|
46 } |
|
47 |
|
48 |
|
49 /* Calculate CRC32 for len bytes from pointer buf with init starting value. */ |
|
50 uint32_t GenerateCRC32Sum(const void* buf, unsigned int len, uint32_t init) |
|
51 { |
|
52 uint32_t crc = ~init; |
|
53 unsigned char* current = (unsigned char*) buf; |
|
54 while (len--) |
|
55 crc = crc32_table[(crc ^ *current++) & 0xFF] ^ (crc >> 8); |
|
56 return ~crc; |
|
57 } |
|
58 |
|
59 /* Calc CRC32 for retain file byte by byte. */ |
|
60 int CheckFileCRC(FILE* file_buffer) |
|
61 { |
|
62 /* Set the magic constant for one-pass CRC calc according to ZIP CRC32. */ |
|
63 const uint32_t magic_number = 0x2144df1c; |
|
64 |
|
65 /* CRC initial state. */ |
|
66 uint32_t calc_crc32 = 0; |
|
67 char data_block = 0; |
|
68 |
|
69 while(!feof(file_buffer)){ |
|
70 if (fread(&data_block, sizeof(data_block), 1, file_buffer)) |
|
71 calc_crc32 = GenerateCRC32Sum(&data_block, sizeof(char), calc_crc32); |
|
72 } |
|
73 |
|
74 /* Compare crc result with a magic number. */ |
|
75 return (calc_crc32 == magic_number) ? 1 : 0; |
|
76 } |
|
77 |
|
78 /* Compare current hash with hash from file byte by byte. */ |
|
79 int CheckFilehash(void) |
|
80 { |
|
81 int k; |
|
82 int offset = sizeof(retain_info.retain_size); |
|
83 |
|
84 rewind(retain_buffer); |
|
85 fseek(retain_buffer, offset , SEEK_SET); |
|
86 |
|
87 uint32_t size; |
|
88 fread(&size, sizeof(size), 1, retain_buffer); |
|
89 if (size != retain_info.hash_size) |
|
90 return 0; |
|
91 |
|
92 for(k = 0; k < retain_info.hash_size; k++){ |
|
93 uint8_t file_digit; |
|
94 fread(&file_digit, sizeof(char), 1, retain_buffer); |
|
95 if (file_digit != *(retain_info.hash+k)) |
|
96 return 0; |
|
97 } |
|
98 |
|
99 return 1; |
|
100 } |
|
101 |
|
102 void InitRetain(void) |
|
103 { |
|
104 int i; |
|
105 |
|
106 /* Generate CRC32 lookup table. */ |
|
107 GenerateCRC32Table(); |
|
108 |
|
109 /* Get retain size in bytes */ |
|
110 retain_info.retain_size = GetRetainSize(); |
|
111 |
|
112 /* Hash stored in retain file as array of char in hex digits |
|
113 (that's why we divide strlen in two). */ |
|
114 retain_info.hash_size = PLC_ID ? strlen(PLC_ID)/2 : 0; |
|
115 //retain_info.hash_size = 0; |
|
116 retain_info.hash = malloc(retain_info.hash_size); |
|
117 |
|
118 /* Transform hash string into byte sequence. */ |
|
119 for (i = 0; i < retain_info.hash_size; i++) { |
|
120 int byte = 0; |
|
121 sscanf((PLC_ID + i*2), "%02X", &byte); |
|
122 retain_info.hash[i] = byte; |
|
123 } |
|
124 |
|
125 /* Calc header offset. */ |
|
126 retain_info.header_offset = sizeof(retain_info.retain_size) + \ |
|
127 sizeof(retain_info.hash_size) + \ |
|
128 retain_info.hash_size; |
|
129 |
|
130 /* Set header CRC initial state. */ |
|
131 retain_info.header_crc = 0; |
|
132 |
|
133 /* Calc crc for header. */ |
|
134 retain_info.header_crc = GenerateCRC32Sum( |
|
135 &retain_info.retain_size, |
|
136 sizeof(retain_info.retain_size), |
|
137 retain_info.header_crc); |
|
138 |
|
139 retain_info.header_crc = GenerateCRC32Sum( |
|
140 &retain_info.hash_size, |
|
141 sizeof(retain_info.hash_size), |
|
142 retain_info.header_crc); |
|
143 |
|
144 retain_info.header_crc = GenerateCRC32Sum( |
|
145 retain_info.hash, |
|
146 retain_info.hash_size, |
|
147 retain_info.header_crc); |
|
148 } |
|
149 |
|
150 void CleanupRetain(void) |
|
151 { |
|
152 /* Free hash memory. */ |
|
153 free(retain_info.hash); |
|
154 } |
|
155 |
|
156 int CheckRetainFile(const char * file) |
|
157 { |
|
158 retain_buffer = fopen(file, "rb"); |
|
159 if (retain_buffer) { |
|
160 /* Check CRC32 and hash. */ |
|
161 if (CheckFileCRC(retain_buffer)) |
|
162 if (CheckFilehash()) |
|
163 return 1; |
|
164 fclose(retain_buffer); |
|
165 retain_buffer = NULL; |
|
166 } |
|
167 return 0; |
|
168 } |
|
169 |
|
170 int CheckRetainBuffer(void) |
|
171 { |
|
172 retain_buffer = NULL; |
|
173 if (!retain_info.retain_size) |
|
174 return 1; |
|
175 |
|
176 /* Check latest retain file. */ |
|
177 if (CheckRetainFile(rb_file)) |
|
178 return 1; |
|
179 |
|
180 /* Check if we have backup. */ |
|
181 if (CheckRetainFile(rb_file_bckp)) |
|
182 return 1; |
|
183 |
|
184 /* We don't have any valid retain buffer - nothing to remind. */ |
|
185 return 0; |
|
186 } |
|
187 |
|
188 #ifndef FILE_RETAIN_SAVE_PERIOD_S |
|
189 #define FILE_RETAIN_SAVE_PERIOD_S 1.0 |
|
190 #endif |
|
191 |
|
192 static double CalcDiffSeconds(IEC_TIME* t1, IEC_TIME *t2) |
|
193 { |
|
194 IEC_TIME dt ={ |
|
195 t1->tv_sec - t2->tv_sec, |
|
196 t1->tv_nsec - t2->tv_nsec |
|
197 }; |
|
198 |
|
199 if ((dt.tv_nsec < -1000000000) || ((dt.tv_sec > 0) && (dt.tv_nsec < 0))){ |
|
200 dt.tv_sec--; |
|
201 dt.tv_nsec += 1000000000; |
|
202 } |
|
203 if ((dt.tv_nsec > +1000000000) || ((dt.tv_sec < 0) && (dt.tv_nsec > 0))){ |
|
204 dt.tv_sec++; |
|
205 dt.tv_nsec -= 1000000000; |
|
206 } |
|
207 return dt.tv_sec + 1e-9*dt.tv_nsec; |
|
208 } |
|
209 |
|
210 |
|
211 int RetainSaveNeeded(void) |
|
212 { |
|
213 int ret = 0; |
|
214 static IEC_TIME last_save; |
|
215 IEC_TIME now; |
|
216 double diff_s; |
|
217 |
|
218 /* no retain */ |
|
219 if (!retain_info.retain_size) |
|
220 return 0; |
|
221 |
|
222 /* periodic retain flush to avoid high I/O load */ |
|
223 PLC_GetTime(&now); |
|
224 |
|
225 diff_s = CalcDiffSeconds(&now, &last_save); |
|
226 |
|
227 if ((diff_s > FILE_RETAIN_SAVE_PERIOD_S) || ForceSaveRetainReq()) { |
|
228 ret = 1; |
|
229 last_save = now; |
|
230 } |
|
231 return ret; |
|
232 } |
|
233 |
|
234 void ValidateRetainBuffer(void) |
|
235 { |
|
236 if (!retain_buffer) |
|
237 return; |
|
238 |
|
239 /* Add retain data CRC to the end of buffer file. */ |
|
240 fseek(retain_buffer, 0, SEEK_END); |
|
241 fwrite(&retain_crc, sizeof(uint32_t), 1, retain_buffer); |
|
242 |
|
243 /* Sync file buffer and close file. */ |
|
244 #ifdef __WIN32 |
|
245 fflush(retain_buffer); |
|
246 #else |
|
247 fsync(fileno(retain_buffer)); |
|
248 #endif |
|
249 |
|
250 fclose(retain_buffer); |
|
251 retain_buffer = NULL; |
|
252 } |
|
253 |
|
254 void InValidateRetainBuffer(void) |
|
255 { |
|
256 if (!RetainSaveNeeded()) |
|
257 return; |
|
258 |
|
259 /* Rename old retain file into *.bak if it exists. */ |
|
260 rename(rb_file, rb_file_bckp); |
|
261 |
|
262 /* Set file CRC initial value. */ |
|
263 retain_crc = retain_info.header_crc; |
|
264 |
|
265 /* Create new retain file. */ |
|
266 retain_buffer = fopen(rb_file, "wb+"); |
|
267 if (!retain_buffer) { |
|
268 fprintf(stderr, "Failed to create retain file : %s\n", rb_file); |
|
269 return; |
|
270 } |
|
271 |
|
272 /* Write header to the new file. */ |
|
273 fwrite(&retain_info.retain_size, |
|
274 sizeof(retain_info.retain_size), 1, retain_buffer); |
|
275 fwrite(&retain_info.hash_size, |
|
276 sizeof(retain_info.hash_size), 1, retain_buffer); |
|
277 fwrite(retain_info.hash , |
|
278 sizeof(char), retain_info.hash_size, retain_buffer); |
|
279 } |
|
280 |
|
281 void Retain(unsigned int offset, unsigned int count, void *p) |
|
282 { |
|
283 if (!retain_buffer) |
|
284 return; |
|
285 |
|
286 /* Generate CRC 32 for each data block. */ |
|
287 retain_crc = GenerateCRC32Sum(p, count, retain_crc); |
|
288 |
|
289 /* Save current var in file. */ |
|
290 fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); |
|
291 fwrite(p, count, 1, retain_buffer); |
|
292 } |
|
293 |
|
294 void Remind(unsigned int offset, unsigned int count, void *p) |
|
295 { |
|
296 /* Remind variable from file. */ |
|
297 fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); |
|
298 fread((void *)p, count, 1, retain_buffer); |
|
299 } |
|
300 #endif // !HAVE_RETAIN |