ce18f524d8559c88ffea0728be174e331c4b7ab0
[profile/ivi/dlt-daemon.git] / src / lib / dlt_filetransfer.c
1 /**
2  * @licence app begin@
3  * Copyright (C) 2012  BMW AG
4  *
5  * This file is part of GENIVI Project Dlt - Diagnostic Log and Trace console apps.
6  *
7  * Contributions are licensed to the GENIVI Alliance under one or more
8  * Contribution License Agreements.
9  *
10  * \copyright
11  * This Source Code Form is subject to the terms of the
12  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed with
13  * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  *
16  * \author Alexander Wenzel <alexander.aw.wenzel@bmw.de> BMW 2011-2012
17  *
18  * \file dlt_filetransfer.c
19  * For further information see http://www.genivi.org/.
20  * @licence end@
21  */
22
23
24 /*******************************************************************************
25 **                                                                            **
26 **  SRC-MODULE: dlt-test-client.c                                             **
27 **                                                                            **
28 **  TARGET    : linux                                                         **
29 **                                                                            **
30 **  PROJECT   : DLT                                                           **
31 **                                                                            **
32 **  AUTHOR    : Alexander Wenzel Alexander.AW.Wenzel@bmw.de                   **
33 **                                                                            **
34 **  PURPOSE   :                                                               **
35 **                                                                            **
36 **  REMARKS   :                                                               **
37 **                                                                            **
38 **  PLATFORM DEPENDANT [yes/no]: yes                                          **
39 **                                                                            **
40 **  TO BE CHANGED BY USER [yes/no]: no                                        **
41 **                                                                            **
42 *******************************************************************************/
43
44 /*******************************************************************************
45 **                      Author Identity                                       **
46 ********************************************************************************
47 **                                                                            **
48 ** Initials     Name                       Company                            **
49 ** --------     -------------------------  ---------------------------------- **
50 **  aw          Alexander Wenzel           BMW                                **
51 *******************************************************************************/
52
53 #include "dlt_filetransfer.h"
54
55 //!Defines the buffer size of a single file package which will be logged to dlt
56 #define BUFFER_SIZE 1024
57
58 //!Defines the minimum timeout between two dlt logs. This is important because dlt should not be flooded with too many logs in a short period of time.
59 #define MIN_TIMEOUT 20
60
61
62 #define DLT_FILETRANSFER_TRANSFER_ALL_PACKAGES LONG_MAX
63
64
65 //!Buffer for dlt file transfer. The size is defined by BUFFER_SIZE
66 unsigned char buffer[BUFFER_SIZE];
67
68
69 //!Get some information about the file size of a file
70 /**See stat(2) for more informations.
71  * @param file Absolute file path
72  * @return Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
73  */
74 unsigned long getFilesize(const char* file){
75         struct stat st;
76         stat(file, &st);
77         return (unsigned long)st.st_size;
78 }
79
80 /** A simple Hash function for C-strings
81  * @param str input string. E.g. a file path.
82  * @param hash start and result value for hash computation
83  *
84  */
85 void stringHash(const char* str, unsigned long *hash )
86 {
87     if (!str || !hash)
88         return;
89    unsigned int len = strlen(str);
90
91    unsigned int i = 0;
92    if (len <= 0){
93     return;
94    }
95
96    for(i = 0; i < len;  i++)
97    {
98       *hash = 53 * *hash  + str[i];
99    }
100
101 }
102
103
104 //!Get some information about the file serial number of a file
105 /** See stat(2) for more informations.
106  * @param file Absolute file path
107  * @return Returns a unique number associated with each filename
108  */
109 unsigned long getFileSerialNumber(const char* file){
110         struct stat st;
111         unsigned long ret;
112         stat(file, &st);
113         ret = st.st_ino;
114         ret = ret << (sizeof(ret)*8)/2;
115         ret |= st.st_size;
116     ret ^= st.st_ctime;
117     stringHash(file, &ret);
118         return ret;
119 }
120
121 //!Returns the creation date of a file
122 /** See stat(2) for more informations.
123  * @param file Absolute file path
124  * @return Returns the creation date of a file
125 */
126 time_t getFileCreationDate(const char* file){
127         struct stat st;
128         stat(file, &st);
129         return st.st_ctime;
130 }
131
132 //!Returns the creation date of a file
133 /** Format of the creation date is Day Mon dd hh:mm:ss yyyy
134  * @param file Absolute file path
135  * @return Returns the creation date of a file
136 */
137 char* getFileCreationDate2(const char* file){
138         struct stat st;
139         stat(file, &st);
140         
141         struct tm  *ts= localtime(&st.st_ctime);
142         return asctime(ts);
143 }
144
145 //!Checks if the file exists
146 /**@param file Absolute file path
147  * @return Returns 1 if the file exists, 0 if the file does not exist
148  */
149 int isFile (const char* file)
150 {
151         struct stat   st;   
152         return (stat (file, &st) == 0);
153 }
154
155 //!Waits a period of time
156 /**Waits a period of time. The minimal time to wait is MIN_TIMEOUT. This makes sure that the FIFO of dlt is not flooded.
157  * @param timeout Timeout to in ms but can not be smaller as MIN_TIMEOUT
158  */
159 void doTimeout(int timeout)
160 {
161         usleep(timeout * 1000);
162 }
163
164 //!Checks free space of the user buffer
165 /**
166  * @param returns -1 if more than 50% space in the user buffer is free. Otherwise 1 will be returned.
167  */
168 int checkUserBufferForFreeSpace()
169 {
170         int total_size, used_size;
171
172         dlt_user_check_buffer(&total_size, &used_size);
173
174         if((total_size - used_size) < (total_size/2))
175         {
176                 return -1;
177         }
178         return 1;
179 }
180
181 //!Deletes the given file
182 /**
183  * @param filename Absolute file path
184  * @return If the file is successfully deleted, a zero value is returned.If the file can not be deleted a nonzero value is returned.
185  */
186 int doRemoveFile(const char*filename){
187         return remove( filename); 
188 }
189
190 void dlt_user_log_file_errorMessage(DltContext *fileContext, const char *filename, int errorCode){
191
192         if(errno != ENOENT)
193         {
194                 DLT_LOG(*fileContext,DLT_LOG_ERROR,
195                         DLT_STRING("FLER"),
196                         DLT_INT(errorCode),
197                         DLT_INT(-errno),
198                         DLT_UINT(getFileSerialNumber(filename)),
199                         DLT_STRING(filename),
200                         DLT_UINT(getFilesize(filename)),
201                         DLT_STRING(getFileCreationDate2(filename)),
202                         DLT_UINT(dlt_user_log_file_packagesCount(fileContext,filename)),
203                         DLT_UINT(BUFFER_SIZE),
204                         DLT_STRING("FLER")
205                 );              
206         } else {
207                 DLT_LOG(*fileContext,DLT_LOG_ERROR,
208                         DLT_STRING("FLER"),
209                         DLT_INT(errorCode),
210                         DLT_INT(-errno),
211                         DLT_STRING(filename),
212                         DLT_STRING("FLER")
213                 );
214         }
215 }
216
217
218
219 //!Logs specific file inforamtions to dlt
220 /**The filename, file size, file serial number and the number of packages will be logged to dlt.
221  * @param fileContext Specific context
222  * @param filename Absolute file path
223  * @return Returns 0 if everything was okey.If there was a failure a value < 0 will be returned.
224  */
225 int dlt_user_log_file_infoAbout(DltContext *fileContext, const char *filename){
226         
227         if(isFile(filename))
228         {
229                 DLT_LOG(*fileContext,DLT_LOG_INFO,
230                         DLT_STRING("FLIF"),
231                         DLT_STRING("file serialnumber"),DLT_UINT(getFileSerialNumber(filename)),
232                         DLT_STRING("filename"),DLT_STRING(filename),
233                         DLT_STRING("file size in bytes"),DLT_UINT(getFilesize(filename)),
234                         DLT_STRING("file creation date"),DLT_STRING(getFileCreationDate2(filename)),
235                         DLT_STRING("number of packages"),DLT_UINT(dlt_user_log_file_packagesCount(fileContext, filename)),
236                         DLT_STRING("FLIF")
237                 );
238                 return 0;
239         } else {
240                 dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_INFO_ABOUT);
241                 return DLT_FILETRANSFER_ERROR_INFO_ABOUT;
242         }
243 }
244
245 //!Transfer the complete file as several dlt logs.
246 /**This method transfer the complete file as several dlt logs. At first it will be checked that the file exist.
247  * In the next step some generic informations about the file will be logged to dlt.
248  * Now the header will be logged to dlt. See the method dlt_user_log_file_header for more informations.
249  * Then the method dlt_user_log_data will be called with the parameter to log all packages in a loop with some timeout.
250  * At last dlt_user_log_end is called to signal that the complete file transfer was okey. This is important for the plugin of the dlt viewer. 
251  * @param fileContext Specific context to log the file to dlt
252  * @param filename Absolute file path
253  * @param deleteFlag Flag if the file will be deleted after transfer. 1->delete, 0->notDelete
254  * @param timeout Timeout in ms to wait between some logs. Important that the FIFO of dlt will not be flooded with to many messages in a short period of time.
255  * @return Returns 0 if everything was okey. If there was a failure a value < 0 will be returned.
256  */
257 int dlt_user_log_file_complete(DltContext *fileContext, const char *filename, int deleteFlag, int timeout)
258 {       
259         if(!isFile(filename))
260         {
261                 dlt_user_log_file_errorMessage(fileContext,filename, DLT_FILETRANSFER_ERROR_FILE_COMPLETE);
262                 return DLT_FILETRANSFER_ERROR_FILE_COMPLETE;
263         }
264         
265         if(dlt_user_log_file_header(fileContext,filename) != 0)
266         {
267                 return DLT_FILETRANSFER_ERROR_FILE_COMPLETE1;
268         }
269                 
270         if(dlt_user_log_file_data(fileContext, filename,DLT_FILETRANSFER_TRANSFER_ALL_PACKAGES,timeout) != 0)
271         {
272                 return DLT_FILETRANSFER_ERROR_FILE_COMPLETE2;
273         }
274                 
275         if(dlt_user_log_file_end(fileContext,filename, deleteFlag) != 0)
276         {
277                 return DLT_FILETRANSFER_ERROR_FILE_COMPLETE3;
278         }               
279                         
280         return 0;
281 }
282
283 //!This method gives information about the number of packages the file have
284 /**Every file will be divided into several packages. Every package will be logged as a single dlt log.
285  * The number of packages depends on the BUFFER_SIZE.
286  * At first it will be checked if the file exist. Then the file will be divided into
287  * several packages depending on the buffer size.
288  * @param fileContext Specific context to log the file to dlt
289  * @param filename Absolute file path
290  * @return Returns the number of packages if everything was okey. If there was a failure a value < 0 will be returned.
291  */
292 int dlt_user_log_file_packagesCount(DltContext *fileContext, const char *filename){
293         int packages;
294         long filesize;
295         
296         if(isFile(filename))
297         {
298                 packages = 1;
299                 filesize = getFilesize(filename);
300                 if(filesize < BUFFER_SIZE)
301                 {       
302                         return packages;
303                 } 
304                 else 
305                 {
306                         packages = filesize/BUFFER_SIZE;
307                         
308                         if(filesize%BUFFER_SIZE == 0)
309                         {       
310                                 return packages;
311                         }
312                         else
313                         {
314                                 return packages+1;
315                         }
316                 }
317         } else {
318                 dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_PACKAGE_COUNT);
319                 return -1;
320         }
321 }
322
323 //!Transfer the head of the file as a dlt logs.
324 /**The head of the file must be logged to dlt because the head contains inforamtion about the file serial number,
325  * the file name, the file size, package number the file have and the buffer size.
326  * All these informations are needed from the plugin of the dlt viewer.
327  * See the Mainpages.c for more informations.
328  * @param fileContext Specific context to log the file to dlt
329  * @param filename Absolute file path
330  * @param alias Alias for the file. An alternative name to show in the receiving end
331  * @return Returns 0 if everything was okey. If there was a failure a value < 0 will be returned.
332  */
333 int dlt_user_log_file_header_alias(DltContext *fileContext,const char *filename, const char *alias){
334
335         if(isFile(filename))
336         {
337                 DLT_LOG(*fileContext,DLT_LOG_INFO,
338                                         DLT_STRING("FLST"),
339                                         DLT_UINT(getFileSerialNumber(filename)),
340                                         DLT_STRING(alias),
341                                         DLT_UINT(getFilesize(filename)),
342                                         DLT_STRING(getFileCreationDate2(filename));
343                                         DLT_UINT(dlt_user_log_file_packagesCount(fileContext,filename)),
344                                         DLT_UINT(BUFFER_SIZE),
345                                         DLT_STRING("FLST")
346                                 );
347
348                 return 0;
349         }
350         else
351         {
352                 dlt_user_log_file_errorMessage(fileContext,filename, DLT_FILETRANSFER_ERROR_FILE_HEAD);
353                 return DLT_FILETRANSFER_ERROR_FILE_HEAD;
354         }
355 }
356
357 //!Transfer the head of the file as a dlt logs.
358 /**The head of the file must be logged to dlt because the head contains inforamtion about the file serial number,
359  * the file name, the file size, package number the file have and the buffer size.
360  * All these informations are needed from the plugin of the dlt viewer.
361  * See the Mainpages.c for more informations.
362  * @param fileContext Specific context to log the file to dlt
363  * @param filename Absolute file path
364  * @return Returns 0 if everything was okey. If there was a failure a value < 0 will be returned.
365  */
366 int dlt_user_log_file_header(DltContext *fileContext,const char *filename){
367
368         if(isFile(filename))
369         {
370                 DLT_LOG(*fileContext,DLT_LOG_INFO,
371                                         DLT_STRING("FLST"),
372                                         DLT_UINT(getFileSerialNumber(filename)),
373                                         DLT_STRING(filename),
374                                         DLT_UINT(getFilesize(filename)),
375                                         DLT_STRING(getFileCreationDate2(filename));
376                                         DLT_UINT(dlt_user_log_file_packagesCount(fileContext,filename)),
377                                         DLT_UINT(BUFFER_SIZE),
378                                         DLT_STRING("FLST")              
379                                 );
380
381                 return 0;
382         }
383         else
384         {
385                 dlt_user_log_file_errorMessage(fileContext,filename, DLT_FILETRANSFER_ERROR_FILE_HEAD);
386                 return DLT_FILETRANSFER_ERROR_FILE_HEAD;
387         }
388 }
389
390 //!Transfer the content data of a file.
391 /**See the Mainpages.c for more informations.
392  * @param fileContext Specific context to log the file to dlt
393  * @param filename Absolute file path
394  * @param packageToTransfer Package number to transfer. If this param is LONG_MAX, the whole file will be transferred with a specific timeout
395  * @param timeout Timeout to wait between dlt logs. Important because the dlt FIFO should not be flooded. Default is defined by MIN_TIMEOUT. The given timeout in ms can not be smaller than MIN_TIMEOUT.
396  * @return Returns 0 if everything was okey. If there was a failure a value < 0 will be returned.
397  */
398 int dlt_user_log_file_data(DltContext *fileContext,const char *filename, int packageToTransfer, int timeout){
399         FILE *file;
400         int pkgNumber;
401         long readBytes;
402                 
403         if(isFile(filename))
404         {
405         
406                 file = fopen (filename,"rb");
407                 if (file == NULL)
408                 {
409                         dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_FILE_DATA);
410                         return DLT_FILETRANSFER_ERROR_FILE_DATA;
411                 }
412                 
413                 if( (packageToTransfer != DLT_FILETRANSFER_TRANSFER_ALL_PACKAGES && packageToTransfer > dlt_user_log_file_packagesCount(fileContext,filename)) || packageToTransfer <= 0)
414                 {
415                         DLT_LOG(*fileContext,DLT_LOG_ERROR,
416                                 DLT_STRING("Error at dlt_user_log_file_data: packageToTransfer out of scope"),
417                                 DLT_STRING("packageToTransfer:"),
418                                 DLT_UINT(packageToTransfer),
419                                 DLT_STRING("numberOfMaximalPackages:"),
420                                 DLT_UINT(dlt_user_log_file_packagesCount(fileContext,filename)),
421                                 DLT_STRING("for File:"),
422                                 DLT_STRING(filename)
423                         );
424                         fclose(file);
425                         return DLT_FILETRANSFER_ERROR_FILE_DATA;
426                 }
427
428                 readBytes = 0;
429                 
430                 if(packageToTransfer != DLT_FILETRANSFER_TRANSFER_ALL_PACKAGES)
431                 {
432 //                              If a single package should be transferred. The user has to check that the free space in the user buffer > 50%
433 //                              if(checkUserBufferForFreeSpace()<0)
434 //                                      return DLT_FILETRANSFER_ERROR_FILE_DATA_USER_BUFFER_FAILED;
435
436                                 fseek ( file , (packageToTransfer-1)*BUFFER_SIZE , SEEK_SET );
437                                 readBytes = fread(buffer, sizeof(char), BUFFER_SIZE, file);
438
439                                 DLT_LOG(*fileContext,DLT_LOG_INFO,
440                                 DLT_STRING("FLDA"),
441                                 DLT_UINT(getFileSerialNumber(filename)),
442                                 DLT_UINT(packageToTransfer),
443                                 DLT_RAW(buffer,readBytes),
444                                 DLT_STRING("FLDA")
445                                 );
446
447                                 doTimeout(timeout);
448
449                 } else {
450                         pkgNumber = 0;
451                         while( !feof( file ) )
452                         {
453 //                              If the complete file should be transferred, the user buffer will be checked.
454 //                              If free space < 50% the package won't be transferred.
455                                 if(checkUserBufferForFreeSpace()>0)
456                                 {
457                                         pkgNumber++;
458                                         readBytes = fread(buffer, sizeof(char), BUFFER_SIZE, file);
459                                 
460                                         DLT_LOG(*fileContext,DLT_LOG_INFO,
461                                                         DLT_STRING("FLDA"),
462                                                         DLT_UINT(getFileSerialNumber(filename)),
463                                                         DLT_UINT(pkgNumber),
464                                                         DLT_RAW(buffer,readBytes),
465                                                         DLT_STRING("FLDA")
466                                         );
467                                 }
468                                 doTimeout(timeout);
469                         }
470                 }
471                 
472                 fclose(file);
473                 
474                 return 0;
475                 
476         } else {
477                 dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_FILE_DATA);
478                 return DLT_FILETRANSFER_ERROR_FILE_DATA;
479         }
480         
481 }
482 //!Transfer the end of the file as a dlt logs.
483 /**The end of the file must be logged to dlt because the end contains inforamtion about the file serial number.
484  * This informations is needed from the plugin of the dlt viewer.
485  * See the Mainpages.c for more informations.
486  * @param fileContext Specific context to log the file to dlt
487  * @param filename Absolute file path
488  * @param deleteFlag Flag to delete the file after the whole file is transferred (logged to dlt).1->delete,0->NotDelete
489  * @return Returns 0 if everything was okey. If there was a failure a value < 0 will be returned.
490  */
491 int dlt_user_log_file_end(DltContext *fileContext,const char *filename,int deleteFlag){
492
493         if(isFile(filename))
494         {
495
496                 DLT_LOG(*fileContext,DLT_LOG_INFO,
497                                 DLT_STRING("FLFI"),
498                                 DLT_UINT(getFileSerialNumber(filename)),
499                                 DLT_STRING("FLFI")
500                 );
501                 
502                 if(deleteFlag){
503                                 if( doRemoveFile(filename) != 0 ){
504                                         dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_FILE_END);
505                                         return -1;
506                                 }
507                 }
508         
509                 return 0;
510         }else{
511                 dlt_user_log_file_errorMessage(fileContext,filename,DLT_FILETRANSFER_ERROR_FILE_END);
512                 return DLT_FILETRANSFER_ERROR_FILE_END;
513         }
514 }