How do I convert s.st_dev to /sys/block/<name> - c++

I want to determine whether a file is on an HDD or an SDD.
I found out that I could check the type of drive using the /sys/block info:
prompt$ cat /sys/block/sdc/queue/rotational
1
This has 1 if it is rotational or unknown. It is 0 when the disk is an SSD.
Now I have a file and what to know whether it is on an HDD or an SDD. I can stat() the file to get the device number:
struct stat s;
stat(filename, &s);
// what do I do with s.st_dev now?
I'd like to convert s.st_dev to a drive name as I have in my /sys/block directory, in C.
What functions do I have to use to get that info? Or is it available in some /proc file?

First of all for the input file we need to file on which partition the file exists
you can use the following command for that
df -P <file name> | tail -1 | cut -d ' ' -f 1
Which will give you output something like this : /dev/sda3
Now you can apply following command to determine HDD , SDD
cat /sys/block/sdc/queue/rotational
You can use popen in your program to get output of these system commands

Okay, I really found it!
So my first solution, reading the partitions, wouldn't work. It would give me sbc1 instead of sbc. I also found the /proc/mounts which includes some info about what's mounted where, but it would still not help me convert the value to sbc.
Instead, I found another solution, which is to look at the block devices and more specifically this softlink:
/sys/dev/block/<major>:<minor>
The <major> and <minor> numbers can be extracted using the functions of the same name in C (I use C++, but the basic functions are all in C):
#include <sys/types.h>
...
std::string dev_path("/sys/dev/block/");
dev_path += std::to_string(major(s.st_dev));
dev_path += ":";
dev_path += std::to_string(minor(s.st_dev));
That path is a soft link and I want to get the real path of the destination:
char device_path[PATH_MAX + 1];
if(realpath(dev_path.c_str(), device_path) == nullptr)
{
return true;
}
From that real path, I then break up the path in segments and search for a directory with a sub-directory named queue and a file named rotational.
advgetopt::string_list_t segments;
advgetopt::split_string(device_path, segments, { "/" });
while(segments.size() > 3)
{
std::string path("/"
+ boost::algorithm::join(segments, "/")
+ "/queue/rotational");
std::ifstream in;
in.open(path);
if(in.is_open())
{
char line[32];
in.getline(line, sizeof(line));
return std::atoi(line) != 0;
}
segments.pop_back();
}
The in.getline() is what reads the .../queue/rotational file. If the value is not 0 then I consider that this is an HDD. If something fails, I also consider that the drive is an HDD drive. The only way my function returns false is if the rotational file exists and is set to 0.
My function can be found here. The line number may change over time, search for tool::is_hdd.
Old "Solution"
The file /proc/partition includes the major & minor device numbers, a size, and a name. So I just have to parse that one and return the name I need. Voilà.
$ cat /proc/partitions
major minor #blocks name
8 16 1953514584 sdb
8 17 248832 sdb1
8 18 1 sdb2
8 21 1953263616 sdb5
8 0 1953514584 sda
8 1 248832 sda1
8 2 1 sda2
8 5 1953263616 sda5
11 0 1048575 sr0
8 32 976764928 sdc
8 33 976763904 sdc1
252 0 4096 dm-0
252 1 1936375808 dm-1
252 2 1936375808 dm-2
252 3 1936375808 dm-3
252 4 16744448 dm-4
As you can see in this example, the first two lines represent the column names and an empty.The Name column is what I was looking for.

Related

Reading records from a file in FORTRAN66 using stdin adding extra unwanted junk

I'm trying to read a file in the format specified below using FORTRAN 66.
1000
MS 1 - Join Grps Group Project 5 5
Four Programs Programming 15 9
Quiz 1 Quizzes 10 7
FORTRAN Programming 25 18
Quiz 2 Quizzes 10 9
HW 1 - Looplang Homework 20 15
I execute and read the file like so:
program < grades.txt
The first line is the total number of points that can be earned in a class
The rest of the lines are assignments in a class
Each line is formatted as such: Assignment name(20 chars) category (20 chars) possible points(14 chars) earned points(14 chars)
For some reason, when the code runs and reads the file, starting at the first assignment record, I get error 5006, and cannot find an explanation of the error code. The output of the program while debugging looks like this:
$ file < grades.txt
MS 1 - Join Grps Group Project 5 6417876
NOT EOF
EOF 5006
NAME CATEGORY POSSIBLE EARNED
My goal is to be able to read each line and put each column into it's appropriate array, then reference those arrays later on to print a report for each category, with each assignment, points possible, earned, and total percentage for the category, then loop, etc.
I do not understand where the "6417876" in the output is coming from, it is definitely not part of the file that's being piped into stdin while the program reads.
The code for the program is as follows:
CHARACTER*20 ASSIGNMENTT(100)
CHARACTER*20 CATEGORY(100)
INTEGER POSSIBLE(100)
INTEGER EARNED(100)
INTEGER TOTALPTS
INTEGER REASON
INTEGER I, N
READ(5,50)TOTALPTS
50 FORMAT(I4)
c Read the arrays in
I=1
100 READ(5,110,IOSTAT=REASON)ASSIGNMENTT(I),CATEGORY(I),POSSIBLE(I),EARNED(I)
110 FORMAT(2A20x,2I14x)
WRITE(*,110)ASSIGNMENTT(I),CATEGORY(I),POSSIBLE(I),EARNED(I)
I=I+1
IF (REASON < 0) GOTO 120
WRITE(*,*)"NOT EOF"
IF (I<100 .AND. REASON == 0) GOTO 100
WRITE(*,*)"EOF", REASON
c Get the number of items (For some reason stdin adds an extra item that's not in the file, so I subtract 2 instead of 1
120 N=I-2
c Display the Names and Ages
WRITE(*,200)
200 FORMAT("NAME",T20,"CATEGORY",T40,"POSSIBLE",T54,"EARNED",T68)
DO 300 I=1,N
210 FORMAT(A20,A20,I14,I14)
300 WRITE(*,210)ASSIGNMENTT(I),CATEGORY(I),POSSIBLE(I),EARNED(I)
END
What could be causing the read issues I'm facing?
The line to read the file contents was too long, so I shortened the names of the variables to save some space and the problem was solved.

bash: clean and merge data

I have three csv files containing different data for a common object. These represent data about distinct collections of items at work. These objects have unique codes. The number of files is not important so I will set this problem up with two. I have a handy recipe for joining these files using join -- but the cleaning part is killing me.
File A snippet - contains unique data. Also the cataloging error E B.
B 547
J 65
EB 289
E B 1
CO 8900
ZX 7
File B snippet - unique data about a different dimension of the objects.
B 5
ZX 67
SD 4
CO 76
J 54
EB 10
Note that file B contains a code not in common with file A.
Now I submit to you the "official" canon of codes designated for this set of objects:
B
CO
ZX
J
EB
Note that File B contains a non-canonical code with data. It needs to be captured and documented. Same with bad code in file A.
End goal: run trend and stats on the collections using the various fields from the multiple reports. They mostly match the canon but there are oddballs due to cataloging errors and codes that are no longer in use.
End goal result after merge/join:
B 547 5
J 65 54
EB 289 10
CO 8900 76
ZX 7 67
So my first idea was to use grep -F -f for this, using the canonical codes as a search list then merge with join. Problem is, with one letter codes it's too inclusive. It would seem like a job for awk where it can work with tab delimiters and REGEX the oddball codes. I'm not sure though, how to get awk to use a list to sift other files. Will join alone handle all this? Maybe I merge with join or paste, then sift out the weirdos? Which method is the least brittle and more likely to handle edge cases like the drunk cataloger?
If you're thinking, "Dude, this is better done with Perl or Python ...etc.". I'm all ears. No rules, I just need to deliver!
Your question says the data is csv, but based on your samples I'm assuming it's tsv. I'm also assuming E B should end up in the outlier output and that NA values should be filled with 0.
Given those assumptions, the following may be sufficient:
sort -t $'\t' -k 1b,1 fileA > fileA.sorted && sort -t $'\t' -k 1b,1 fileB > fileB.sorted
join -t $'\t' -a1 -a2 -e0 -o auto fileA.sorted fileB.sorted > out
grep -f codes out > out-canon
grep -vf codes out > out-oddball
The content of file codes:
^B\s
^CO\s
^ZX\s
^J\s
^EB\s
Result:
$ cat out-canon
B 547 5
CO 8900 76
EB 289 10
J 65 54
ZX 7 67
$ cat out-oddball
E B 1 0
SD 0 4
Try this(GNU awk):
awk 'BEGIN{FS=OFS="\t";}ARGIND==1{c[$1]++;}ARGIND==2{b[$1]=$2}ARGIND==3{if (c[$1]) {print $1,$2,b[$1]+0; delete b[$1];} else {if(tolower($1)~"[a-z]+ +[a-z]+")print>"error.fileA"; else print>"oddball.fileA";}}END{for (i in b) {print i,0,b[i] " (? maybe?)";print i,b[i] > "oddball.fileB";}}' codes fileB fileA
It will create error.fileA, oddball.fileA if such lines exists, oddball.fileB.
Normal output didn't write to file, you can write with > yourself when results are ok:
B 547 5
J 65 54
EB 289 10
CO 8900 76
ZX 7 67
SD 0 4 (? maybe?)
Had a hard time reading your description, not sure if this is what you want.
Anyway it's easy to improve this awk code.
You can change to FILENAME=="file1", or FILENAME==ARGV[1] if ARGIND is not working.

Proper reading of MP3 file disrupted by ID3 tags

My semestral project is due this Thursday and I have major problem with reading MP3 file (the project is about sound analysis, don't ask my what exactly is it about and why I'm doing it so late).
First, I read first 10 bytes to check for ID3 tags. If they're present, I'll just skip to the first MP3 header - or at least that's the big idea. Here is how I count ID3 tag size:
if (inbuf[0] == 'I' && inbuf[1] == 'D' && inbuf[2] == '3') //inbuf contains first 10 bytes from file
{
int size = inbuf[3] * 2097152 + inbuf[4] * 16384 + inbuf[5] * 128 + inbuf[6]; //Will change to binary shifts later
//Do something else with it - skip rest of ID3 tags etc
}
It works ok for files without ID3 tags and for some files with them, but for some other files ffmpeg (which I use for decoding) returns "no header" error, which means it didn't catch MP3 header correctly. I know that since if I remove ID3 from that .mp3 file (with Winamp for example), no errors occur. The conclusion is that size count algorithm isn't always valid.
So the question is: how do I get to know how big exactly is entire ID3 part of the .mp3 file (all possible tags, album picture and whatever)? I'm looking for it everywhere but I just keep finding this algorithm I posted above. Sometimes also something about some 10 bytes footer I need to take into account, but it seems it frequently gets more than 10 bytes for it to eventually catch proper MP3 frame.
The size of an ID3v1 Tag is always fixed 128 Bytes.
I will find the following description
If you one sum the the size of all these fields we see that 30+30+30+4+30+1 equals 125 bytes and not 128 bytes. The missing three bytes can be found at the very beginning of the tag, before the song title. These three bytes are always "TAG" and is the identification that this is indeed a ID3 tag. The easiest way to find a ID3v1/1.1 tag is to look for the word "TAG" 128 bytes from the end of a file.
Source: http://id3.org/ID3v1
There is another version, called ID3v2:
One of the design goals were that the ID3v2 should be very flexible and expandable...
Since each frame can be 16MB and the entire tag can be 256MB you'll probably never again be in the same situation as when you tried to write a useful comment in the old ID3 being limited to 30 characters.
This ID3v2 always starts at the begin of an audio file, as you can read here: http://id3.org/ID3v2Easy
ID3v2/file identifier "ID3"
ID3v2 version $03 00
ID3v2 flags %abc00000
ID3v2 size 4 * %0xxxxxxx
The ID3v2 tag size is encoded with four bytes where the most significant bit (bit 7) is set to zero in every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 02 01.
bool LameDecoder::skipDataIfRequired()
{
auto data = m_file.read(3);
Q_ASSERT(data.size() == 3);
if (data.size() != 3)
return false;
if (memcmp(data.constData(), "ID3", 3))
{
m_file.seek(0);
return true;
}
// ID3v2 tag is detected; skip it
m_file.seek(3+2+1);
data = m_file.read(4);
if (data.size() != 4)
return false;
qint32 size = (data[0] << (7*3)) | (data[1] << (7*2)) |
(data[2] << 7) | data[3];
m_file.seek(3+2+1+4+size);
return true;
}

Parse ASCII Output of a Device-File in C++

i wrote a kernelspace driver for a USB-device. If it is connected it mounts to /dev/myusbdev0 for example.
Via command line with echo -en "command" > /dev/myusbdev0 i can send commands to the device and read results with cat /dev/myusbdev0.
Ok, now i have to write a C++ program. At first i would open the device file for read/write with:
int fd = open("/dev/echo", O_RDRW);
After that a cmd will be send to get the device working:
char cmd[] = { "\x02sEN LMDscandata 1\x03" };
write(fd, cmd, sizeof(cmd));
Now i get to the part i dont now how to handle yet. i now need to read from the device, as its keeping on sending me data continously. this data i need to read and parse now ...
char buf[512];
read(fd, buf, sizeof(buf);
The data looks like following, each one starts with \x02 and ends with \x03, they are not always the same size:
sRA LMDscandata 1 1 89A27F 0 0 343 347 27477BA9 2747813B 0 0 7 0 0
1388 168 0 1 DIST1 3F800000 00000000 186A0 1388 15 8A1 8A5 8AB 8AC 8A6
8AC 8B6 8C8 8C2 8C9 8CB 8C4 8E4 8E1 8EB 8E0 8F5 908 8FC 907 906 0 0 0
0 0 0 All Values are separated with a 20hex {SPC}
it think i need some kind of while loop to continiously read the data from an \x02 until i read a \x03.
if i have a complete scan, i need to parse this ascii message in its seperate parts (some variables uint_16, uint_8, enum_16, ...).
any idea how i can read a complete scan into a buf[] and then parse its components out?
As you say the device is sending continiously, i would recommend adding a queue to hold the chunks coming in, and some dispatching that takes out parts of the queue, i.e. x02 to x03, decoupling the work that is done from receiving chunks.
Furthermore you can have then single objects handling one complete block from x02 to x03, perhaps threaded (makes sense with the information given).
device => chunk reader => input queue => inputer reader => data handling
hope this helps

Can any one tell me how to write a simple C++ code to export my data(from variable) into PDF file without using any external libraries or utilities?

Can any one tell me how to write a simple C++ code to export my data(from variable) into PDF file without using any external libraries or utilities?
Use fprintf and write to a file, conforming to the PDF file format.
Specifically look in Appendix G. You might be able to get away with some very simple transformations and produce a readable PDF file.
Without using a PDF library your best way might be to create a simple PDF with sample text using some other application (eg OpenOffice). Then use this as a basis for the boiler plate and have you C++ code insert your own text in place. Note that PDF is line orientated so each new line of text has to be explicitly placed at the correct position.
For anything other than very simple output this is going to be tricky without fully understanding the PDF spec
Can any one tell me how to write a simple C++ code to export my data(from variable) into PDF file without using any external libraries or utilities?
No. PDF files are complicated. To read and write them, your code is going to be complicated. Normally, you could use an external library to hide that complexity, but if you don't want to do that, then your code is going to have to contain the complexity. And then it will not be simple.
You're asking for the impossible. You're asking to perform a complex task without doing anything complex yourself, and without relying on anyone else to do something complex for you.
But someone is going to have to do it.
I'm currently using ZendPDF right now, and I have created a test pdf file that looked like this when viewed as text. When I changed the text "This is a test" to "This is a super test" in the pdf file itself, I was able to change the final pdf file and it opened in foxit. So, copy this and use C++ to replace the string with what you want. I was also able to make new text appear with just a text editor by copying that F1 14 Tf stuff.
The php used to output this text is
<?php
require("inc/function.inc.php");
// start authorization check
require("inc/authcheckhdr.inc.php");
$pdf = new Zend_Pdf();
// Fetch the first page so you can draw on top of it.
$page = $pdf->pages[0] = $pdf->newPage('A4');
// Set font and text color.
$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA), 14);
$page->setFillColor(new Zend_Pdf_Color_HTML('black'));
// Draw some text on top of the page at x,y
$page->drawText('This is a test', 72, 720);
// More drawing commands...
// Save the resulting PDF document. OR
$pdf->save('result.pdf');
// Render the resulting PDF and send it to the browser
$pdfDocument = $pdf->render();
header("Content-Type: application/pdf");
header("Content-Disposition: attachment; filename=result.pdf");
echo $pdfDocument;
?>
The pdf file is...
%PDF-1.4
%âãÏÓ
1 0 obj
<</Type /Catalog /Version /1.4 /Pages 2 0 R /Names 7 0 R >>
endobj
2 0 obj
<</Type /Pages /Kids [3 0 R ] /Count 1 >>
endobj
3 0 obj
<</Type /Page /LastModified (D:20091231114504-08'00') /Resources <</ProcSet [/Text /PDF ] /Font <</F1 8 0 R >> >> /MediaBox [0 0 595 842 ]
/Contents [4 0 R ] /Parent 2 0 R >>
endobj
4 0 obj
<</Length 50 >>
stream
/F1 14 Tf
0 g
BT
72 620 Td
(This is a test) Tj
ET
/F1 14 Tf
0 g
BT
72 720 Td
(This is a super test) Tj
ET
endstream
endobj
5 0 obj
[]
endobj
6 0 obj
<</Names 5 0 R >>
endobj
7 0 obj
<</Dests 6 0 R >>
endobj
8 0 obj
<</Type /Font /Encoding /WinAnsiEncoding /Subtype /Type1 /BaseFont /Helvetica >>
endobj
xref
0 9
0000000000 65535 f
0000000015 00000 n
0000000091 00000 n
0000000149 00000 n
0000000341 00000 n
0000000441 00000 n
0000000460 00000 n
0000000494 00000 n
0000000528 00000 n
trailer
<</ID [<65386133343365653231383339346131> <64663632306233616663343536616332> ] /Size 9 /Root 1 0 R >>
startxref
625
%%EOF
...without using any external
libraries or utilities?
that is a bit of a problem... If you are using QT you can do it... What is your setup?